-
Notifications
You must be signed in to change notification settings - Fork 494
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
[WIP] sokol_spritebatch.h #534
base: master
Are you sure you want to change the base?
Conversation
An XNA-style spritebatch library on top of sokol_gfx. Relies on premultiplied alpha for blending. Takes care of orthographic projection internally. Sorts the sprites that have been submitted to ensure the fewest draw calls are made.
Hey @nyalloc, Coming from XNA/MonoGame, I would advise against having a batcher which sorts draw calls. I would consider sorting a separate concern to merging/batching draw calls for textured quads. Christer Ericson has blogged about sorting draw calls: https://realtimecollisiondetection.net/blog/?p=86. As you can see sorting draw calls is something that will be more or less unique to the needs of the game. I.e., it is likely to be more than just sorting by texture/depth. XNA's Additionally, some ideas floating around for some improvements for a better API for XNA's SpriteBatch:
Just my 2 cents. |
That looks great! The for your work on this. A user-defined sorting key would be great (for shaders and other states). Another example of a unique spritebatcher is https://github.com/RandyGaul/cute_headers/blob/master/cute_spritebatch.h It builds the sprite atlas as well. |
Reworked the API to use contexts. Removed internal sorting of sprites (for now, at least). Adjusted naming. Introduced push_sprite_rect which lets you create a sprite to be rendered at a specific destination rectangle.
Hello! Back on this now. As per @lithiumtoast feedback I've removed the sorting API, right now users will be capable of worting their sprites externally in a manner that is appropriate for their application. I can always reintroduce this if there is ever demand for it, but removing it does simplify the API and the implementation quite a bit. New in these changes are the ability to render to different render targets + use different sg_pipelines. I'm still working on these parts of the API and I think it will be a little while before I get them right. Luckily there is plenty of examples of this in the other sokol utility headers. Regarding the 3x2 matrices, I think this is an interesting idea. @NoelFB uses a 3x2 matrix stack for his spritebatcher and this is something I can see being quite handy without adding very much overhead (3x2 matrix multiply against 2d points is pretty trivial). I'll continue working on this and throw up a sample application when it is ready. |
Unfrotunately sokol's 0 initialise to default idiom does not play nicely with color data, as 0'd out sg_color would ideally be set to a sensible default, white. However, 0'd out sg_color is also a valid color, transparent, which can result in undesirable effects if you are lerping color to transparent.
Btw, trying to do this myself with EDIT: OpenGL and DirectX use the same memory layout however, forget about row/column major order after that. Here's a image visually explaining the affine transforms possible for 2D. Notice the greyed out last row which is always the same. This is why a 3x2 matrix is more compact for 2D instead of just a 3x3. |
It shouldn't matter if sokol-shdc struggles with 3x2 matrices, the library won't need them for the shaders. If I introduce a 3x2 matrix stack to this, then that multiplication will be done CPU-side when generating the vertex buffer. This is fine, because the 3x2 matrix multiplication is really trivial. NoelFB's batcher demonstrates this pretty well. |
Ah, I was going for doing it all in the vertex shader:
|
That is neat. The vertex buffer takes the form of an array of matrices that define the sprite dimensions / transform? I wonder how you would handle texture coordinates or other per-vertex state. Right now it's not clear to me if there would be a meaningful performance benefit to this appraoch so I'm not going to change course. A worthwhile experiment though. Let me know how it goes. |
By using instancing, the single vertex buffer is the "sprite batch items"; each vertex is the instance data for the textured quad.
It's done by using the source rectangle position and size along with a uniform for the texture size. However, the consequence is that sprite batching would have to be fragmented by texture. Thus, using a different texture requires stopping the current batch in process and starting a new batch. For me, this is acceptable because I would be using a texture atlas. Here's the whole shader program I'm using with
|
the sbatch_pipeline helps users make a pipeline object that is correct and usable for the sbatch rendering API.
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.
Some things I stumbled over when testing on macOS, also some minor things in sokol-samples (will comment on those now).
Vertex shader snaps sprites to the nearest whole pixel
return _sbatch_pack_color_bytes(r, g, b, a); | ||
} | ||
|
||
static int _sg_image_slot_index(uint32_t id) { |
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.
Copy-paste bug? (shouldn't this be _sbatch_image_slot_index())
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 doesn't appear to be fixed yet?
sg_draw(base_element, num_elements, 1); | ||
} | ||
|
||
static void _sbatch_matmul(sbatch_matrix* p, const sbatch_matrix* a, const sbatch_matrix* b) { |
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.
The function _sbatch_matmul() doesn't appear to be used anywhere?
Allow sprites to be optionally tilted 45 degrees for depth-based custom pipelines.
A work-in-progress XNA-style spritebatch library on top of sokol_gfx.
Sample PR
Sample video
The library iterates over all the sprites submitted and creates a batch when the texture of the current sprite is different from the previous sprite. Making sure the same texture is used as often as possible, via texture atlas and/or via appropriate use of sorting is good for performance.
Shaders and pipeline are currently taken care of internally, but eventually an API will be provided to allow users to override the default pipeline with a custom one. This will enable users to change things like the blend state or shader being used, which will be advantageous for developing custom effects like additive sprite-based lighting or applying post-processing to a fullscreen quad.
Like XNA, this library relies on premultiplied alpha for alpha blending. Ideally this would be handled offline by your project's content pipeline, however, a function is provided (
sb_premultiply_alpha
) to post-process texture data before it is used to create ansg_image
.