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
Optimizations to experimental geoshader Sprite #905
base: master
Are you sure you want to change the base?
Conversation
a840c63
to
cb60424
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've suggested changes which will save contributors and users time in the future, as well as some style nitpicks.
@@ -309,5 +309,7 @@ def invalidate(self): | |||
until the next time the buffer is used, for efficiency). | |||
""" | |||
buffer = self.buffer | |||
buffer._dirty_min = min(buffer._dirty_min, self.start) | |||
buffer._dirty_max = max(buffer._dirty_max, self.end) | |||
if self.start < buffer._dirty_min: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explaining why we aren't using the more concise syntax will save everyone time by preventing reversion attempts.
if self.start < buffer._dirty_min: | |
# Intentionally avoid min and max functions to increase performance as of 3.8 - 3.12 | |
if self.start < buffer._dirty_min: |
""" | ||
For each attribute, create a ctypes array pointing to the entire buffer | ||
which can be used to write values into the buffer using slice assignment | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor style nitpick: pyglet's docstring conventions place the summary on the same line as the opening quotes.
""" | |
For each attribute, create a ctypes array pointing to the entire buffer | |
which can be used to write values into the buffer using slice assignment | |
""" | |
"""For each attribute, create a ctypes array pointing to to its entire buffer. | |
This allows using slice assignment syntax to write values into the buffer. | |
""" |
pyglet/graphics/vertexdomain.py
Outdated
""" | ||
Returns a function that, when called, sets the values for a given attribute. | ||
|
||
The setter becomes invalid when the VertexList migrates to a different | ||
domain, and it's the caller's responsibility to stop using the old setter | ||
and create a new one. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Same docstring style nitpick as before
- Phrasing tweaks
- We can make debugging easier by explaining what happens when you fail to migrate to the new setter so the issue can be identified
""" | |
Returns a function that, when called, sets the values for a given attribute. | |
The setter becomes invalid when the VertexList migrates to a different | |
domain, and it's the caller's responsibility to stop using the old setter | |
and create a new one. | |
""" | |
"""Return a function which sets the value(s) of the named attribute. | |
The setter is invalidated whenever the VertexList migrates to a different | |
domain. It's the caller's responsibility to stop using the old setter and | |
create a new one when this happens. | |
""" |
Slots may have to be omitted as this will break existing code or pushed further into a release where we can break compatibility. Personally I am not a fan of restricting sprites to this degree in general. Although it's probably fine for a new class such as |
Yeah, I talked a little bit about the slots change in the PR description, though didn't call it out by name. It's the bit saying "Some of these changes are bad and should not be merged! ..." |
…s bound `functools.partial()`
…n` Custom `VertexList` subclass instead of `VertexList.__getattr__`/`__setattr__`
…s bound closure. Custom `VertexList` subclass instead of `VertexList.__getattr__`/`__setattr__`
c950120
to
631c70b
Compare
@benmoran56 I cherry-picked your code that uses a custom VertexList subclass per VertexDomain. Benchmark results are below. The fastest is to combine our two optimizations, which is weird considering the closure/partial-bound |
…ial, reduce memory usage
`vertex_list_set_attribute_values` along with a data tuple to set values | ||
for the attribute. | ||
|
||
These params become invalid when the VertexList migrates to a different |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a blocker, unfortunately. For the default Batch, users should be able to add/remove/migrate things between Batches transparently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is safe for Sprite
to use internally, right?
Sprite._batch
and Sprite._vertex_list
are private. For a user to migrate a Sprite between batches, they have to assign to Sprite.batch
, Sprite.program
, etc. And all those properties call VertexList.get_setter_params
.
If a user is using VertexDomain
/ VertexList
directly, they can safely avoid get_setter_params
and instead use the same API that sprites do today: get a BufferObjectRegion
via VertexList.__getattr__
or the generated properties on _vertexlist_class
.
After learning about pyglet's Sprite, and learning that pyglet's drawables are generic such that shapes and sprites can be combined into the same batch, I wondered how it compares against arcade's Sprite. My benchmarks showed pyglet is slower, so I dug into the code and made some optimizations. These optimizations make pyglet's sprite faster than arcade's in my specific benchmark, creating thousands of sprites and moving each one every frame.
I made a variety of changes in separate commits, then ran the same benchmark against each commit. This table was generated by hyperfine and shows the relative performance of each. The most recent (fastest) commit is at the top, and the bottom commit is the vanilla geoshader Sprite currently on master.
Some of these changes are bad and should not be merged! I was curious if they would help, but the benchmark shows that they did not improve performance, and may have made it worst. I've included them here to share my research.
By looking at the relative timings, you can see which code changes had a positive/negative/negligible impact on performance.
This PR probably should not be merged as-is. Rather, the most impactful performance improvements can be extracted to another PR.
To run the benchmark yourself, open
bench.sh
and put in the correct commit hash where it tells you to. Then runbench.sh
and see the results inresults.md
. On my windows box in PowerShell, I run it like this: