Do you create separate immutable constant buffers (ID3D11Buffer*) for each material instance (StateGroup in your terminology from other thread) during loading?
Yeah, pretty much. The material file contains a blob of bytes (or several of these blobs of bytes -- if the shader splits its parameters between more than one cbuffer), which are copied into actual buffer objects when the file is loaded. I actually store more than one material in a file (I have "material pack" files), and when building these packs, I compare different material's blobs against each other to remove duplicates, so multiple materials may actually share an immutable buffer if they contain the same values.
I also want to experiment with only creating a few dynamic buffer objects and copying data from memory into them before using each material, just to see how this performs in comparison. D3D9 doesn't support cbuffers, so I use that approach in D3D9 (copying these blobs from memory into the uniform registers before draw calls).
How is your StateGroup structure defined in code?
It's a variable-length blob of bytes, which starts with a header structure that describes the total length and a bitfield of the states contained. After the header is a series of state 'packets', which each start with a state-ID and then some data (such as D3D pointers). They're parsed like you would parse a file or a network packet.
How do you resolve pointers to graphics resources when loading StateGroups?
The state groups are stored on disc in the same format that they'll be used in memory, so they're just read in and cast to the right type. However, some of the state packets may contain pointers, which obviously can't be saved in the file. So, in place of the pointer, there will be some other data written, usually an integer index of the same size.
e.g. a material-pack might first create an array of ID3D11Buffer*'s. It then parses it's StateGroups, looking for any "SetCBuffer" packets in them.
In these packets, in place of a ID3D11Buffer* there will be written an integer that is an index into the material-pack's array of buffers. The appropriate pointer is fetched from this array using the index value, and then this pointer is written into the packet, over the top of that integer.
Pretty standard "pointer-patching" stuff for in-place loading.
With temporary data that is only required during loading and then discarded (like blobs of bytes that are immediately copied into D3D buffers and no longer used), these are stored in a different "part" of my data file. The loading system will create a different memory allocation for each "part" of the file, so that the main part can be kept around, but the "temp data" part can be free'ed after parsing is complete.
Does your low-level platform-independent graphics layer have functions like UpdateConstantBuffer(), SetConstantBuffers(), SetSamplers(), SetTextures() ?
Yes, there's an API for updating cbuffers.
The "set" commands are not directly exposed though. There is a StateGroupWriter class (used to create StateGroups), which contains these "set" commands. It takes the arguments and constructs state packets inside a state-group blob, which it returns to the user.
The user cannot set states directly, instead when they want to draw something, they submit the draw-call along with an array of StateGroups. This way, the "state machine" nature of the underlying API is hidden, and the user doesn't have to worry about whether some particular state was set previously or not (states cannot "leak" from one draw-call into the next).
Edited by Hodgman, 17 May 2013 - 12:54 AM.