Hello,
So, I am getting serious about a shader compiler and related things now, and I've made a design proposal.
First of all, let us collect a few things we want regarding shader handling: -> D3DXCompileShader & Friends -> D3DXAssembleShader & Friends -> D3DXDisassembleShader & Friends -> A way to deal with d3d9 and d3d10 shader language differences
All these items are related in some way, so I am proposing the following design of a shader library:
The core part of the library is a wine internal shader language representing D3D assembler shaders. Shaders in this language can be created from D3D Bytecode("SlDisassembleShader"), D3D ASM("SlAssembleShader") and from HLSL("SlCompileShader"). From the intermediate language, D3D bytecode can be generated in various versions(SlGenBytecode), as well as D3D Text asm(SlGenAsm). Unlike regular D3D asm languages, this language does not have a version token, it is a superset of all asm language versions. It does have a pixel / vertex / geometry shader flag though.
The motivation behind an intermediate language is to avoid writing many different code generators, which is a big part of the HLSL compiler. We also need a WineD3D intermediate language to share our shader generator between d3d9 and d3d10, and use it for both kinds of shaders. The requirements for both intermediate asm languages are the same. This motivates making the language the same to reuse code.
With those functions, the D3DX functions are pretty streightforward: D3DXAssembleShader: bytecode = SlGenBytecode(SlAssembleShader(char *asm_text));
D3DXCompileShader: bytecode = SlGenBytecode(SlCompileShader(char *hlsl_text));
D3DXDisassembleShader asm_text = SlGenAsm(SlDisassembleShader(DWORD *bytecode));
WineD3D can be changed to use this shader language as shader input. In this case, d3d8, d3d9 and d3d10 run their shader bytecode through SlDisassembleShader, and pass the returned pointer to WineD3D. This in turn calls SlCopyShader to make a copy it owns, and d3d8/9/10 free their copy using SlDestroyShader.
With WineD3D working on the same language, we have to share the language definition between the shader library and WineD3D, or put the shader compiler into WineD3D as well. I do not like the 2nd way, as it puts things together that don't have to be in one lib, and wastes runtime memory. A 3rd way I have to consider was a SlReadShader() function with callbacks which takes over the current role of shader_generate_main() in WineD3D. I am not quite convinced by this either.
Attached are a few files for reference: shaderlib.gif: A diagram explaining the roles of the languages(No, it is not UML 2.0) uses.gif: Illustrating various uses of the shader library shader-lang: A very early pseudocode definition of the language
The intermediate asm language follows the same principles as D3D bytecode. A shader contains any number of instructions, each instruction has one destination register and any number of source registers. The instructions have an opcode, writemask, modifiers. A register has a type, an index, information about relative addressing, source modifiers, and a swizzle.
Appart of the instructions, the shader contains a header with informations about the shader. Declarations, input/output semantics, special registers used, etc. This replaces the shader_get_registers_used() pass in WineD3D. It is done by the shader library, and it also generates the declarations for 1.x pixel shaders. (We still have to find out which texture type to read from in wined3d).
Thanks, Stefan