-
Notifications
You must be signed in to change notification settings - Fork 160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change the rules of how game objects are updated after sprite's update/deletion #2117
Change the rules of how game objects are updated after sprite's update/deletion #2117
Conversation
@messengerbag, in regards to crashing the engine in case of user error, curiously, I missed this earlier, but found when testing these changes. There are already 2 checks for invalid sprites when drawing room objects and characters only. So, this pr brings back this old behavior probably, where deleting a sprite and forgetting to reassign a new one will stop the game with error message, like:
I may leave this for the time being, although I'd wish that such reaction was consistent among all the things. What will happen for other things in general, is that they will display the "placeholder" instead. Which is currently a 1x1 transparent sprite. |
c556b0a
to
6a7367d
Compare
6a7367d
to
0653d45
Compare
This breaks some things, need to do more testing. |
Hmm okay, I did some stupid oversights here, and gui is not updated along with dynamic sprites. Also, I see now there's probably ways to simplify this further. So I have to redo half of this pr. UPDATE: I am currently rethinking the whole "sprite was modified" notification part. There are several possible approaches here, and I'm experimenting with them. It's also possible that I need to make few other changes first to make things simpler in code. |
0653d45
to
c5c2e53
Compare
I did some thinking and code research again, and realized that for the texture-based rendering most of the object checking and marking for update is simply not needed anymore. After introducing shared textures (even before the texture cache, but more so after) the objects will already get updated as soon as you update the texture data itself, or mark it deleted. This refers to characters, room objects and overlays. GUI is different though (see below). Then there is software drawing mode. My thinking was: so when you have a shared texture, you may mark texture updated, and all objects that use it will receive this update. So why not do something similar in software mode too? GUI: this is the only major problem left. Because they are still drawn differently, sometimes combining a sprite with their own drawing (and some have >1 sprite, like sliders that have base graphic and a handle graphic), they would need a special approach, which I did not invent yet. For now I left them updated as before: whenever a dynamic sprite is changed or deleted it will run through all guis and controls that may have sprites, and marks those that have this sprite assigned. I've got a feeling that GUIs may also use this "notification block" somehow, I will think about it for bit more. Of course this PR must be compared for performance with the master branch.
|
Note: this does not need #2123, but merging that first will simplify some code here. |
c5c2e53
to
3734e5e
Compare
I did performance tests, and judging by them, the primary culprit in slowing things down was checking properties and resetting them on sprite deletion. I presume that Views did the most impact, in a regular p&c game. Removing these checks and property reassignments alone already seem to give good results. In comparison to that, disabling searching through all GUIs and marking those containing deleted dynamic sprite for redraw makes an almost unnoticeable effect (maybe couple of % gain in fps, in "unlimited fps" mode). For the reference: I've been testing a real game scene where around 200+ temporary dynamic sprites were created and deleted per game frame, which contained 90 GUIs and 380 buttons, each of them was checked on each sprite deletion. So while it would be good to improve this further, and only check the guis that matter, I think it would be already fine even like it is now. If I won't be able to figure out a nice way to handle the gui notification... NOTE: I prefer to merge this after #2123, because the code will become bit simpler. |
Currently placeholder is a 1x1 transparent sprite. Placeholder is returned by the operator[] for safety purposes. DoesSpriteExist() is still a valid way to test if a sprite is actually registered. Removed the obscure mechanic of remapping failed sprites to sprite 0.
3734e5e
to
7c4fcdc
Compare
Previous existing method ran through virtually all the game objects that may reference a sprite, and checked each one of them. Those who did reference the modified dynamic sprite were marked for update and/or had their draw caches cleared. Furthermore, for safety reasons, the Graphic properties of these objects were reset to 0. This process was slow under two conditions: - if there are alot of objects (especially overlays which may be created at runtime in large quantities); - if there are alot of temporary dynamic sprites created and deleted per game frame (for example, when drawing something complicated). The new method: 1. We no longer reset object properties, this turned to be a bad design. There's another safety fix done in the SpriteCache, which returns a placeholder sprite for any invalid sprite ID. This should make things simpler and faster. 2. For the texture-based renderers, it turned out that updating the shared texture entries was enough to make objects using them redraw. So for these renderers we don't need to reset any more caches. 3. For software renderer, introduced a GameState::spritemodified vector of bools. Whenever a sprite is modified or deleted, engine sets a corresponding flag in this vector, and that triggers an update at the next draw pass for all the objects that still reference that sprite. GUI PROBLEM: because GUIs rendering is still different from other objects, even in texture render mode, they are still marked for update the old way. This should be addressed separately in the future.
7c4fcdc
to
0b84a21
Compare
So, in regards to software mode, I redid back to the variant which uses vector of bools to mark "recently modified sprites". This might be faster to check regularly at a cost of extra memory use. Also it is just simpler code-wise. From what I heard before, the |
The recent version of this doesn't error in Future Flashback anymore. I tried some other games and they all seem to work well. I tried to compare performance but can't tell any regression - it looks the same? At least on the test games I had. The only one I didn't test was the sandwalker because it's using ags4, but I assume it's not significantly impacted because it doesn't do sprite deletion at any point - afaict. |
The truth is, this becomes noticeable (at least on systems with a good CPU) only when the game creates and deletes a lot of dynamic sprites at once; but also has alot of objects where these sprites may be present (like lots of characters, or lots of Views with many frames in them). Like in AGD's "Mage's Initiation" there's a particle system for magic spells which may repeatedly create and delete 200+ dynamic sprites each frame (it's not done very efficiently). That's also why I don't like to spend much time on this... |
Fixes #2104 . Please read the ticket and comments for detailed explanation of the problem.
There are following changes done here.
SpriteCache now has a "placeholder" field initialized to a 1x1 transparent bitmap by default. operator
[]
is ensured to always return this placeholder if an invalid sprite was requested. This is a safeguard against program exceptions. Note that it's still possible to test for sprite's existence explicitly by calling DoesSpriteExist.I think it may be feasible to add a method for setting custom placeholder. Possibly a copy of sprite 0 may be assigned as a placeholder. I may add this behavior if someone thinks that's a good idea. At first I thought to just use sprite 0 implicitly, but it's a real possibility that sprite 0 does not exist in game. So I went with a dedicated member instead. After all it's a exception safeguard, so should work regardless of the game data.
Deleting dynamic sprite no longer resets Graphic property of objects which had it set, at all. The property values stays as they are, which may result in them referring to non-existing sprite, or another dynamic sprite, would the same ID be assigned to them further.
In regards to updating objects on screen after sprite's change or deletion, I implemented a new method that does that. The details may be found in commit called "rewrote dynamic sprite change notification method". In short: for texture-based render updating shared texture seem to be enough. For software mode there's a vector that tells when the sprites were modified. The elements are marked when the sprite is updated or deleted. The game objects test these flags along with other conditions that tell whether object has been modified and should be redrawn. This is done for all objects except for GUI (see below).
NOTE: I found a potential problem with this new method, which is explained in this comment. In short, some object properties or actions may depend on sprite's size, and if they cache sprite's size, or any other value derived from the sprite's size, then the objects also have to be updated as soon as sprite is resized. This may currently occur upon DynamicSprite.Resize, Rotate and ChangeCanvasSize.
Examining current objects, it seems like this is not an immediate problem, as the sprite size is never cached in them (it's only cached in draw caches, but that is still updated properly in this pr). In case this becomes a problem in the future, there remains a solution: track sprite use upon a Graphic property assignment too.
Potential TODO:
For now I'm interested to know if other contributors could see actual flaws in this approach.
Also, this has to be tested for any performance changes. I guess there should be 2 kinds of tests: