11 hours ago, Jemme said:
But that means each DrawCommand is passing in the entire PSO structure (as in the state's you want) with each command and storing it, just for you to sort by the key and elect the first object to bind its PSO for the rest within the group to use. It seems like a lot of wasted memory to pass all the PSO in to use just one, although it does prevent any slow down from swapping PSO for every single object.
I wrote the system in your "Designing a Modern GPU Interface" link
My draw items have an 8 byte header (containing PSO data) followed by a resource binding table -- 8 bytes for the input assembler bindings (though this should really be smaller), 2 bytes per cbuffer, 1 byte per dynamic sampler state, 2 bytes per group of textures. Most draw items end up under 32 bytes. If you put the items themselves into a queue, then the queue's size is ~8 to 80 bytes * num items. If you put pointers to the items into the queue, then there's 8 bytes per item (size of a pointer).
If you have a stateful rendering API, but still use a sorting queue, you probably still put pointers to some kind of "drawable" into that queue, so again, 8 bytes per item of queue storage space is required
The other comparison to keep in mind, is that objects in a stateful graphics engine still need to store their PSO/texture/buffer pointers somewhere. When a tree draws itself, it needs to bind the "bark" and "leaves" textures to the GPU, regardless of whether you're using a stateful or stateless abstraction. The most straightforward way is for each tree model instance to contain a pointer to a material, which itself contains two texture pointers ("bark" and "leaves"). That's 8 bytes (1 pointer) per model instance for the material pointer, and then 16 bytes (2 pointers) within the shared material.
With my draw items, each tree draw-item contains a 2-byte resource-list ID, referencing a shared resource list that contains two 2-byte texture ID's ("bark" and "leaves").
In that made-up comparison, my stateless API actually uses less memory than the stateful system based on pointers ![:) :)](https://uploads.gamedev.net/emoticons/smile.png)
12 hours ago, Jemme said:
Is this not causing the same problem though? as you are passing all the state commands within a DrawCommand object for them to be set during the draw call? yes you are hiding the state machine by not exposing the functions directly but you are just deferring the state changes to the command queue.
It solves the problem of state leakage, because every draw-item has a full description of all pipeline-state/resources. There is no way for states/resources from an earlier draw to accidentally be applied to a later draw.
To avoid re-setting states on each draw, I rely heavily on XOR operations. E.g. in D3D11 you have blend, depth-stencil, and raster states. I represent these with small ID's, which are all packed into the 8-byte draw header. If I XOR the current draw item with the previous one and then check if the bits that represent these ID's are zero, then I know whether those pipeline states have changed or not. There's some similar tricks used for resources -- e.g. CBuffer resources are represented as 2-byte IDs, and using SSE intrinsics, you can XOR an array of these ID's in one CPU instruction to very quickly identify if the cbuffer bindings need to be updated or can be skipped.
In a stateful API:
* the best case is that the rendering logic is very carefully structured by a human, in a way where the minimal amount of pipeline/resource binding updates occur thanks to careful reasoning. However, this is hard to maintain over time when changing the rendering code...
* the worst case is that, as strange bugs start occurring, the rendering programmers start getting paranoid and redundantly setting every single state per draw anyway... and then adding a redundant call filter (e.g. if new blend state == previous, do nothing) however, these will typically be slower than the centralized/optimized/small redundant state filtering code that's at the heart of a stateless API ![:) :)](https://uploads.gamedev.net/emoticons/smile.png)