This page outlines our plan to support Shader Model 2.0+ for both pixel and vertex shaders.
See also DirectX-ToDo
Some of the shader opcodes needed for shader versions 2 and higher are not accessible through ARB_vertex_program which is how we are currently implementing vertex and pixel shaders <= 1.4. To get this functionality, we either need to implement them via software (high CPU usage) or via GLSL (OpenGL Shading Language).
How does Wine's pixel/vertex shader code work?
- There are plenty of sources about pixel (aka "fragment") and vertex shaders available online, so this overview will assume that the reader knows what shaders are. This is meant to be a discussion of how Wine implements DirectX shaders.
- Wine receives bytecode tokens from the application representing the compiled pixel or vertex shader program.
The bytecode is typically compiled using Microsoft's d3d9x_##.dll libraries which are shipped with the application. Wine doesn't currently implement these DLLs (since the apps can legally distribute them), but we could do so in the future. We would have to compile a text file with Microsoft's HLSL or Shader Assembly code into either the same bytecode, or a meta-code that Wine can understand.
- At the moment, this bytecode is processed in the following files:
baseshader.c - Shared, common code between both pixel and vertex shaders.
pixelshader.c - Specific to pixel shaders.
vertexshader.c - Specific to vertex shaders.
We know that the bytecode represents from MSDN: See Shader Tokens
- How bytecode is handled
Historically only vertex shaders (D3D8 ones, VS1.1) were running, using software rendering (pixel shaders will likely be too slow and tricky to simulate).
- WineD3D currently have 2 paths: one for Hardware (HW) support (on top of OpenGL) one for Software (SW) simulation
- SW simulation
In this case, we are decoding and interpreting each bytecode token at each drawPrim() call (in drawprim.c) and for each vertex.
See vertexshader.c, IWineD3DVertexShaderImpl_ExecuteSW() for emulator main entry.
The emulator will get a WINEVSHADERINPUTDATA on input filled by Direct3DDeviceImpl_FillVertexShaderInputSW based on the current IWineD3DVertexDeclaration and current vertex, device and texturing information.
Execution will result in creating a WINEVSHADEROUTPUTDATA structure (vertex position and direction, texturing info, fogging)
drawPrim() only have to draw the vertex (using software rendering draw_vertex())
- Anyway, it is currently disabled and incomplete (only supports VS1.1). Having this fully implemented would be useful for debugging translation errors that may crop up, but it will take some work to finish.
- HW rendering
In this case, we are translation the bytecode tokens into either OpenGL's ARB_vertex_program/ARB_fragment_program strings, or a GLSL program using ARB_shader_objects (work in progress).
- In this mode, behavior is like original DirectX:
Bytecode conversion to OpenGL languages (ie. textuals formats) and OpenGL compilation is done when Shaders are initialised (ie. only once, see IWineD3DVertexShaderImpl_SetFunction)
OpenGL shaders are only loaded in drawPrimitiveDrawStrided() (on need)
In theory, everything that is supported in DirectX shaders should have an OpenGL equivalent, or at least, we should be able to string some operations together to get the same effect. That is our goal, at least.
Roadmap for GLSL implementation
Merge as much functionality as we can from vertexshader.c and pixelshader.c into baseshader.c to avoid duplication of code where possible.
Rename the current GenerateProgramArbHW() functions to baseshader.c: GenerateBaseShader() and decide just before that whether to generate an ARB_vertex_program shader or a GLSL shader.
Place pixel/vertex specific code in header & footer functions which call GenerateBaseShader() (most of the "guts" of that function are identical between PS and VS, so merge the code). We need to do the following first:
Rename all of the "TEMP T" temporaries in vertexshader.c to use the letter R instead so that we can merge the declarations at the beginning of the ARB shader. Every reference to the T variable should be changed to R.
Don't merge the final "END" call and null-termination in GenerateBaseShader() because we need to call "MOV result.color, R0" for pixel shaders version <= 1.4. Leave that in the shader-specific function.
- Mapping to new GLSL functions:
Add another field/column in the opcode tables which specifies a pointer to a function similar to how the pshader_hw_???() functions work. This new function pointer should specify which function needs to be called in order to generate the proper GLSL string for that opcode.
- Implement all of the GLSL strings for each opcode.
Many of these are done.
Determine all of the places in the code that use the shader programs currently (I think it's only in the drawprim.c functions?) and modify them to be able to bind either the GLSL program or the ARB program depending on which is being used.
- drawprim.c functions that need modifications:
drawPrimitiveDrawStrided() - Need to load GLSL Uniforms instead of ARB Constants
This should be determined at run-time based on the existing GL extension.
Should probably also be an option in the registry, at least until everything works 100%.
- Ideally, (once it's finished) we'll probably want to use GLSL unless otherwise specified or if the users card doesn't support it.
- drawprim.c functions that need modifications:
- As of git on October 29th, 2007 GLSL is the default when available. If you want you can disable it using the registry key:
HKEY_CURRENT_USER\Software\Wine\Direct3D\UseGLSL -> "disabled" (it is case sensitive)
Need to finish implementing all of the pixel shader instructions, and get textures other than 2D working properly for shaders < 2.0.
- Need to finish implementing all of the flow control instructions (if, else, endif, etc.)
Need to add support for integer and boolean constants.
- Current Bugs:
Shaders that use more than 15 vertex attributes will fail with the error that you've used more than 16 attributes. Not sure if this is driver bug, or if GLSL counts one of the built-in attributes as being used implicitly, though we never reference it. You can see this behaviour in 3DMark 2001.
- I think this is because when we call glVertex*(), that is implicitly an attribute. Then, we try to declare 16 more which fails. We could probably substitute whichever attribute represents the position (usually v0) with gl_Vertex instead of attrib#.
- Apps that use pixel shaders 1.x may be wrong in both ARB and GLSL because we're currently hard-coding them to use a specifc texture dimension while it could change any time. This could be fixed by verifying the texture dimensions whenever the shader is changed or a texture is changed. This wiki entry has a patch attached which assigns dummy textures to unused dimensions to make wrong sampling sources visible.
- WineD3D eats a number of shader constants for emulating d3d/opengl differences. This causes issues for games that use the maximum number of shader constants (especially SM3.0 games). This causes issues on Geforce6/7 and Radeon X1*00 cards which offer 256 vertex constants of which Wine eats easily 20 and the games expect they can use all 256..
- Please add any comments or questions here or send them to the wine-devel mailing list.