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