"Oh, I'm sure it can't be that bad... can it?" Well, so far, the Xbox's documentation on how it's pixel shaders work are a bit vague. The XDK also supports "Hard Coded" pixel shaders via the D3DPIXELSHADERDEF structure. Here's the definition of it (the X_ prefix was added for Cxbx to prevent collisions):
typedef struct _X_D3DPIXELSHADERDEF
{
DWORD PSAlphaInputs[8]; // Alpha inputs for each stage
DWORD PSFinalCombinerInputsABCD; // Final combiner inputs
DWORD PSFinalCombinerInputsEFG; // Final combiner inputs (continued)
DWORD PSConstant0[8]; // C0 for each stage
DWORD PSConstant1[8]; // C1 for each stage
DWORD PSAlphaOutputs[8]; // Alpha output for each stage
DWORD PSRGBInputs[8]; // RGB inputs for each stage
DWORD PSCompareMode; // Compare modes for clipplane texture mode
DWORD PSFinalCombinerConstant0; // C0 in final combiner
DWORD PSFinalCombinerConstant1; // C1 in final combiner
DWORD PSRGBOutputs[8]; // Stage 0 RGB outputs
DWORD PSCombinerCount; // Active combiner count (Stages 0-7)
DWORD PSTextureModes; // Texture addressing modes
DWORD PSDotMapping; // Input mapping for dot product modes
DWORD PSInputTexture; // Texture source for some texture modes
DWORD PSC0Mapping; // Mapping of c0 regs to D3D constants
DWORD PSC1Mapping; // Mapping of c1 regs to D3D constants
DWORD PSFinalCombinerConstants; // Final combiner constant mapping
}X_D3DPIXELSHADERDEF;
At first glance, the use of each field looks a bit obvious, but transforming this into a pixel shader isn't a trivial task (at least not for me). So far, it's just now starting to make sense (I had to look in the header file instead of the XDK docs). In the end, all pixel shaders on Xbox take the form of the structure you see above.
For those who have been following Xbox emulation history as long as I have, you might recall the day that _SF_ (author of Xeon) released the source code to his Xbox -> PC Direct3D pixel shader conversion code in an effort to prove that his emulator was not fake. I took a look to see how _SF_ did it in Xeon. Turns out that instead of using assembly, he used HLSL instead. It was a brilliant idea, but it's not going to work for Cxbx since it still uses DirectX 8.1 (another reason why I really want to start using OpenGL soon). Assembly shaders will still be possible, but just a bit more tedious.
What have I done so far? I've already written some prelimary code for pixel shader generation. For now, the best thing to do is to test it against pixel shaders written by myself and XDK demos to ensure accuracy. So far, I've written 3 pixel shader examples. Nothing fancy. I've also written some code to output the contents of the structure. They vary from shader to shader. Here's the results.
shader 1:
xps.1.0 // Xbox PixelShader
mov r0, v0 // Move the diffuse vertex colour to the
// output colour register (r0).
output:
PSAphaInputs[8] = 0xD4301010 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSFinalCombinerInputsABCD = 0x00000000
PSFinalCombinerInputsEFG = 0x00000000
PSConstant0[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSConstant1[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSAlphaOutputs[8] = 0x000000C0 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSRGBInputs[8] = 0xC4200000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCompareMode = 0x00000000
PSFinalCombinerConstant0 = 0x00000000
PSFinalCombinerConstant1 = 0x00000000
PSRGBOutputs[8] = 0x000000C0 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCombinerCount = 0x00011101
PSTextureModes = 0x00000000
PSDotMapping = 0x00000000
PSInputTexture = 0x00000000
PSC0Mapping = 0xFFFFFFFF
PSC1Mapping = 0xFFFFFFFF
PSFinalCombinerConstants = 0x000001FF
shader 2:
xps.1.0 // Xbox PixelShader
tex t0 // Declare texture register t0 (Texture Stage 0)
mul r0, v0, t0 // Multiply v0 and t0 the output register (r0)
output:
PSAphaInputs[8] = 0xD4D81010 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSFinalCombinerInputsABCD = 0x00000000
PSFinalCombinerInputsEFG = 0x00000000
PSConstant0[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSConstant1[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSAlphaOutputs[8] = 0x000000C0 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSRGBInputs[8] = 0xC4C80000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCompareMode = 0x00000000
PSFinalCombinerConstant0 = 0x00000000
PSFinalCombinerConstant1 = 0x00000000
PSRGBOutputs[8] = 0x000000C0 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCombinerCount = 0x00011101
PSTextureModes = 0x00000001
PSDotMapping = 0x00000000
PSInputTexture = 0x00000000
PSC0Mapping = 0xFFFFFFFF
PSC1Mapping = 0xFFFFFFFF
PSFinalCombinerConstants = 0x000001FF
shader 3:
xps.1.0 // Xbox PixelShader
tex t0 // Declare texture register t0 (Texture Stage 0)
tex t1 // Declare texture register t0 (Texture Stage 1)
mov r1, t1 // Move texture register t1 into output register r1
lrp r0, v0, t0, r1 // Lerp between t0 and r1 by proportion
// specified in v0
output:
PSAphaInputs[8] = 0xD9301010 0x14D8DD34 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSFinalCombinerInputsABCD = 0x00000000
PSFinalCombinerInputsEFG = 0x00000000
PSConstant0[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSConstant1[8] = 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSAlphaOutputs[8] = 0x000000D0 0x00000C00 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSRGBInputs[8] = 0xC9200000 0x04C8CD24 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCompareMode = 0x00000000
PSFinalCombinerConstant0 = 0x00000000
PSFinalCombinerConstant1 = 0x00000000
PSRGBOutputs[8] = 0x000000D0 0x00000C00 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PSCombinerCount = 0x00011102
PSTextureModes = 0x00000021
PSDotMapping = 0x00000000
PSInputTexture = 0x00000000
PSC0Mapping = 0xFFFFFFFF
PSC1Mapping = 0xFFFFFFFF
PSFinalCombinerConstants = 0x000001FF
So obviously enough, the contents of the structure will vary on the contents of your shader. Quite frankly, it reminds me of .fx files. They are very convienent to use, but once again, not available in D3D8.
Now that I think about it, there is one more option for this... Cg! IMO Cg is awesome shader language (I don't understand why so many hate it) and it supports Direct3D 8.1 too! It's not very different from HLSL either. Although it should work fine on ATI cards, it will make Cxbx much more NVIDIA oriented than it already is.
So, that's what I've been working on lately. I really hope that I can get this pixel shader stuff working sometime. Turok is looking a bit "lifeless" without it's lighting shaders emulated properly. Lack of pixel shaders also limit Cxbx's bumpmapping support too. I'll be sure to keep you all posted on what's going on with this latest endeavour.
Shogun.
EDIT: Sorry, blogger is not very code friendly.
Wow! You have been busy, Shogun! I really enjoy seeing the progress made on this emulator, every step of the way.
ReplyDelete"Although it should work fine on ATI cards, it will make Cxbx much more NVIDIA oriented than it already is." Hahaha. Looks like the gtx 275 I am ordering will come in handy after all.
-Mr. Fabulous
Not to mention that pixel shader support will be a very significant step once its finally implemented.
ReplyDelete-Mr. Fabulous
This is very good!
ReplyDeleteGood News! ;D
ReplyDelete