2013/8/1 Matteo Bruni matteo.mystral@gmail.com:
Instead of generating an entry for the struct with the correct members, the compiler generates TWO entries for sbnf, one with all its fields in D3DXRS_FLOAT4 and the other with D3DXRS_BOOL. Which, if I'm reading this correctly, makes 0 sense. Calling GetConstantByName() on the various fields then happen to return the first instance of the struct. It's the same with native d3dx9, FWIW.
I take this partially back, actually it might make sense after all. Calling GetConstantDesc in general returns an array of D3DXCONSTANT_DESC, not necessarily just one. I always wondered why but I failed to make the connection to this case, until now.
Long wall of text follows, sorry. Case in point:
// Registers: // // Name Reg Size // ------------ ----- ---- // sb b0 3 // snb b3 3 // sbn b6 2 // sbnf b8 1 // sn c0 3 // sbn c3 3 // sbnf c6 3 // sbnf2 c9 3 // sbnf3 c12 3 // snb c15 2
Notice how there is an overallocation of the constants: for sbnf there are 3 float AND one bool registers allocated. Now, checking again +d3dx output but tracing some more stuff:
trace:d3dx:parse_ctab_constant_type name sbnf, elements 1, defaultvalue 0x18e1d0 trace:d3dx:parse_ctab_constant_type regset D3DXRS_FLOAT4, regidx 6, regcount 3 trace:d3dx:parse_ctab_constant_type class D3DXPC_STRUCT, type D3DXPT_VOID, rows 1, columns 3, elements 1, struct_members 3 trace:d3dx:parse_ctab_constant_type name b4, elements 1, defaultvalue 0x18e1d0 trace:d3dx:parse_ctab_constant_type regset D3DXRS_FLOAT4, regidx 6, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_BOOL, rows 1, columns 1, elements 1, struct_members 0 trace:d3dx:parse_ctab_constant_type name n4, elements 1, defaultvalue 0x18e1e0 trace:d3dx:parse_ctab_constant_type regset D3DXRS_FLOAT4, regidx 7, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_INT, rows 1, columns 1, elements 1, struct_members 0 trace:d3dx:parse_ctab_constant_type name f4, elements 1, defaultvalue 0x18e1f0 trace:d3dx:parse_ctab_constant_type regset D3DXRS_FLOAT4, regidx 8, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_FLOAT, rows 1, columns 1, elements 1, struct_members 0 trace:d3dx:parse_ctab_constant_type name sbnf, elements 1, defaultvalue 0x18e124 trace:d3dx:parse_ctab_constant_type regset D3DXRS_BOOL, regidx 8, regcount 1 trace:d3dx:parse_ctab_constant_type class D3DXPC_STRUCT, type D3DXPT_VOID, rows 1, columns 3, elements 1, struct_members 3 trace:d3dx:parse_ctab_constant_type name b4, elements 1, defaultvalue 0x18e124 trace:d3dx:parse_ctab_constant_type regset D3DXRS_BOOL, regidx 8, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_BOOL, rows 1, columns 1, elements 1, struct_members 0 trace:d3dx:parse_ctab_constant_type name n4, elements 1, defaultvalue 0x18e128 trace:d3dx:parse_ctab_constant_type regset D3DXRS_BOOL, regidx 9, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_INT, rows 1, columns 1, elements 1, struct_members 0 trace:d3dx:parse_ctab_constant_type name f4, elements 1, defaultvalue 0x18e12c trace:d3dx:parse_ctab_constant_type regset D3DXRS_BOOL, regidx 9, regcount 0 trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_FLOAT, rows 1, columns 1, elements 1, struct_members 0
Sure enough, if you call GetConstantDesc() for sbnf, passing an array of two D3DXCONSTANT_DESC and setting the count to 2, you get both filled. Same with its struct members. Here's what I get from a quickly hacked test with native d3dx9:
shader.c:6243: sbnf (0) registerset is 2. shader.c:6244: sbnf (0) registerindex is 6. shader.c:6245: sbnf (0) registercount is 3. shader.c:6243: sbnf (1) registerset is 0. shader.c:6244: sbnf (1) registerindex is 8. shader.c:6245: sbnf (1) registercount is 1.
shader.c:6243: sbnf.b4 (0) registerset is 2. shader.c:6244: sbnf.b4 (0) registerindex is 6. shader.c:6245: sbnf.b4 (0) registercount is 1. shader.c:6243: sbnf.b4 (1) registerset is 0. shader.c:6244: sbnf.b4 (1) registerindex is 8. shader.c:6245: sbnf.b4 (1) registercount is 1.
shader.c:6243: sbnf.n4 (0) registerset is 2. shader.c:6244: sbnf.n4 (0) registerindex is 7. shader.c:6245: sbnf.n4 (0) registercount is 1. shader.c:6243: sbnf.n4 (1) registerset is 0. shader.c:6244: sbnf.n4 (1) registerindex is 9. shader.c:6245: sbnf.n4 (1) registercount is 0.
shader.c:6243: sbnf.f4 (0) registerset is 2. shader.c:6244: sbnf.f4 (0) registerindex is 8. shader.c:6245: sbnf.f4 (0) registercount is 1. shader.c:6243: sbnf.f4 (1) registerset is 0. shader.c:6244: sbnf.f4 (1) registerindex is 9. shader.c:6245: sbnf.f4 (1) registercount is 0.
The 0 or 1 in parentheses indicate that the line refers to the first or the second D3DXCONSTANT_DESC respectively. Apparently the registers assigned to a struct are allocated to its members in order, until they run out. This also matches with the output from fxc, if you look at the generated code in particular. On MSDN, in the page for GetConstantDesc, it mentions something about RegisterCount being 0 in some cases (then failing to say anything meaningful on the when and the why). I guess this is pretty much it.
I haven't tested it, but I also expect that setting the value of one of those struct members via the constant table (e.g. SetFloat(device, "sbnf.f4", f)) updates all the "instances" of the constant.
As for the why it is done like this, I guess that's because the compiler might potentially need to have the same constant in multiple registersets (because of the lack of orthogonality between the D3D opcodes and the data types). But it's just a guess.
It looks like quite a number of changes to our ID3DXConstantTable are necessary for this. As far as I can see, the current code for computing the register count for struct members is okay already. What's missing is the ability to store and return multiple D3DXCONSTANT_DESC for each constant and to update all the locations on calls to the various Set* functions as well (assuming that's what actually happens). There might be more but I can't think of anything else right now. Probably it's best to start with fixing / adding tests...
Cheers, Matteo.