2013/8/1 Rico Schüller kgbricola@web.de:
On 31.07.2013 00:14, Matteo Bruni wrote:
2013/7/30 Rico Schüller kgbricola@web.de:
Hi Matteo,
please see the attached patch.
On 25.07.2013 16:13, Matteo Bruni wrote:
2013/7/24 Rico Schüller kgbricola@web.de:
dlls/d3dx9_36/tests/shader.c | 308 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+)
This is okay, but as a followup can you add some tests with mixed-type structs? Something like:
struct { float f; int i; bool b; };
If you have already written tests of this kind, I'd like to know what does the compiler do in this case :)
Single variables could only have the tested types (I was not able to generate other conversions than bool->bool, int->int, int->float, bool->float, float->float). But I found a way to do it with structs and there I found some issues. Hence this has to be fixed in wine, too. Thanks for the nice question. :-)
Basically you got these for the struct:
- D3DXRS_FLOAT4: if one variable is used as float or a float variable is
used or an int variable is used as bool (the compiler may do some optimization), else #2 2. D3DXRS_BOOL: if a bool variable is used as bool (in an if clause), else #3 3. D3DXRS_INT4
It looks like you could only do it that way with unused variables. I'm not sure if this makes sense at all. Why would someone set an unused variable? Maybe I missed something? Do you know anything else?
It does make some sense, although this is not what I expected. Also, I'm getting different results...
If I understand correctly your test, all the fields of a structure share the same registerset. Which is silly, since AFAIU each member of the structure has a separate D3DXCONSTANT_DESC in the constant table, both on disk and in memory, there is no point the compiler should force the same registerset for all the struct members.
Yes, they share all the same registerset. The optimization seems to force only the type of the register (it optimizes the not used members out).
Under the constraint of forcing all the members in the same registerset, the "conversion rules" you mention make sense. In SM3 an if bool can be replaced by an if_comp with a float register and a rep/loop, which is controlled by an integer constant, can be emulated via loop unrolling (although I'm not sure how the compiler can possibly do that for the shader in your testcase). These are also pretty much the only use cases of bool and int constants and there are no int or bool "non-constant" registers so essentially no other type conversion is possible. You can check the plain text output from fxc to see how those constants are used in the shader code and how did the compiler manage to convert those constants from one type to another.
I tried to compile your HLSL shader myself (I had to disable optimization though, otherwise compilation fails) and, assuming the text output of fxc matches what actually ends up in the shader bytecode, in general I'm getting different struct members in different registersets. E.g. snbf gets allocated to c6-c9 and b8. FWIW, I used fxc from the June 2010 DirectX SDK, on Windows 7. I'm not sure why my results are different from yours. Or am I misunderstanding the test?
The bytecode should match the result in the text output. Also after some additional test these structs seem to set several registers (as could be seen in the text), so the constant table constant desc is not able to give a full description for these. So an app should not depend on those or did I miss something? It's getting tricky again. E.g.: struct {float f2; int n2; bool b2;} snb; sets: D3DXRS_FLOAT4 15,16 (60-68) D3DXRS_BOOL 3,4,5 (3-6)
I'll send an update to the tests to cover these issue. I think I have to look at the binary and whats really in there. Hopefully that information could be found somewhere ...
The constant table could easily represent the situation by specifying different register sets for different struct members. But apparently the generated constant table is just broken. I ran the test with WINEDEBUG=d3dx and this is an excerpt of the trace I got:
trace:d3dx:parse_ctab_constant_type name sbnf, elements 1, index 6, defaultvalue 0x18e1d0, regset D3DXRS_FLOAT4 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, index 6, defaultvalue 0x18e1d0, regset D3DXRS_FLOAT4 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, index 7, defaultvalue 0x18e1e0, regset D3DXRS_FLOAT4 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, index 8, defaultvalue 0x18e1f0, regset D3DXRS_FLOAT4 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, index 8, defaultvalue 0x18e124, regset D3DXRS_BOOL 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, index 8, defaultvalue 0x18e124, regset D3DXRS_BOOL 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, index 9, defaultvalue 0x18e128, regset D3DXRS_BOOL 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, index 9, defaultvalue 0x18e12c, regset D3DXRS_BOOL trace:d3dx:parse_ctab_constant_type class D3DXPC_SCALAR, type D3DXPT_FLOAT, rows 1, columns 1, elements 1, struct_members 0
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.
So this looks even more broken than we thought for struct constants. You're right when you say that the application can't depend on this. I assume that this means the application has to explicitly force each field to a known location via the "register" keyword in the shader, or (and this would be AWESOME) just blindly set the shader constants which happened to be used by those fields in previous compilation of the shader. Great...
I'm also using the June 2010 SDK tools, but on wine. The problem might be, that "fixme:d3d9:Direct3DShaderValidatorCreate9 stub" will not fail here. If I disable optimization, I get (I think the same you got): // 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
This is what I get (header.fx is the text file containing the shader) for the shader in the test without disabling anything: wine fxc.exe /E main /Tvs_3_0 header.fx // Registers: // // Name Reg Size // ------------ ----- ---- // sb b0 3 // snb b3 3 // sbn b6 3 // sbnf b9 2 // sn i0 3 // sbnf3 i3 3 // sbnf2 i6 2 // sbnf c0 3 // sbnf2 c3 3 // sbnf3 c6 3
I'm using this receipt to generate the binary blob: wine fxc.exe /E main /Tvs_3_0 /Fo temporary.fxo header.fx 1>2 od -v -t x4 -A n -w32 temporary.fxo | sed 's/ /, 0x/g' | sed 's/^, //g' | sed 's/$/,/g'
Yeah, that matches my results.
It doesn't make a difference if you pass a valid or invalid shader to the constant table interface. I think both should work fine. The question is, why does the compiler produce code which may fail on your system? I guess this will fail on any machine... are there systems where this code works? Or is it the nature of the compiler to produce only sometimes code which works when using optimization?
Well, probably the optimizations are just broken. On the other hand the compiler would produce an error in these cases (but only on Windows and also not a particularly helpful error message... oh well). BTW, the shader generated by disabling the validation would fail at CreateVertexShader() time on Windows (when it would be validated again).
BTW, what needs to be fixed in Wine? I couldn't see anything obvious by reading the test.
I got 104 test failures with this test case. Those are only in the part when converting float <-> int <-> bool... Yeah, you couldn't see those from just reading the code. ;-)
I've attached a new test with a shader binary without optimization. The results are the same, only the register usage changed (and the shader size).
Cheers Rico
I see. Not sure how much that actually matters, since setting the bool field via the constant table wouldn't do the right thing anyway. I guess it depends on the specific tests failing.
Matteo.