Closed abaire closed 2 years ago
Vertex shader:
MUL oT0.x, v0.wxyz, c[120].zx + MUL R0.yzw, v0.wxyz, c[120].zx
MOV R0.x, c[125].z
DP4 R11.z, R0.yzwx, c[98]
DP4 R11.x, R0.yzwx, c[96]
DP4 R11.y, R0.yzwx, c[97]
ADD R10.xyzw, v2, c[129]
DP4 oPos.w, R0.yzwx, c[99]
MUL R10.xyzw, c[121], R10 + MOV oPos.xyz, R11
MUL oT0.y, v1.w, c[120].z + RCC R1.x, R12.w
MUL oPos.xyz, R12.xyz, c[58].xyz
MAD oFog.xyzw, R11.z, c[108].x, c[108].y
MUL oD0.w, R10.w, c[125].x
MOV oD0.xyz, R10
MAD oPos.xyz, R12.xyz, R1.x, c[59].xyz
Filtered for the bits that contribute to oPos
:
MUL R0.yzw, v0.wxyz, c[120].zx
MOV R0.x, c[125].z
DP4 R11.z, R0.yzwx, c[98]
DP4 R11.x, R0.yzwx, c[96]
DP4 R11.y, R0.yzwx, c[97]
DP4 oPos.w, R0.yzwx, c[99]
MOV oPos.xyz, R11
RCC R1.x, R12.w
MUL oPos.xyz, R12.xyz, c[58].xyz
MAD oPos.xyz, R12.xyz, R1.x, c[59].xyz
Some initial values:
v0: 89, 5156, -151, 417
c[58] 320.00, -240.00, 16777215.00, 0.00
c[59] 320.03125, 240.03125, 0.00, 0.00
c[96] 1.00, 0.00, 0.00, 0.00
c[97] 0.00, 1.00, 0.00, 0.00
c[98] 0.00, 0.00, 1.00, 0.00
c[99] -16.8876857758, -18.2681617737, 3.6487216949, 1.00
c[120] 1.00, 0.0000610352, 0.0009765625, 1.00
c[125] 1.00, 1.00, 1.00, 0.00
Some values stepping through with the above inputs (in xemu):
MUL R0.yzw, v0.wxyz, c[120].zx
MOV R0.x, c[125].z
> R0 = 1, 89, 5156, -151
DP4 R11.z, R0.yzwx, c[98]
DP4 R11.x, R0.yzwx, c[96]
DP4 R11.y, R0.yzwx, c[97]
> R11 = 89, 5156, -151, 0
DP4 oPos.w, R0.yzwx, c[99]
MOV oPos.xyz, R11
> oPos = 89, 5156, -151, -96243.6015625
RCC R1.x, R12.w
> R1.x = -0.0000103903
MUL oPos.xyz, R12.xyz, c[58].xyz
> oPos = 28480, -1237440, -2533359360, -96243.6015625
MAD oPos.xyz, R12.xyz, R1.x, c[59].xyz
> oPos = 319.7353210449, 252.8886260986, 26322.3671875,-96243.6015625
Surprisingly, running this test with the same shader and same inputs on HW gives the same unusual values:
So it would appear that this may not be a problem with the programmable part of the vertex shader but rather with how xemu is processing the negative w coord?
I see unhandled pgraph commands between 0x1e80 and 0x1e90 in the logs but they only happen after some of the draws. They also happen in a potentially interesting sequence; vertex shader constants start loading at 0x60 (96, the start of the typical user-configurable constant range), then 0x1e80,84,88,8c, 90, then constants continue loading at 0x7D (125). The values passed to the 1e80-8c vary, but 1e90 is always called with 0x3C (60).
I did investigated this game last year with no progress. but your finding are meaningful. pgraph methods 1e80 to 1e8c are NV097_SET_TRANSFORM_DATA and 1e90 is NV097_LAUNCH_TRANSFORM_PROGRAM so clearly these methods are used to transform certain vertices. too bad these methods are not implemented yet. for 1e80~1e8c, they are easy, simple register read and write. 1e90 is the key.
ok, after a quick reverse engineering and study, here I have some findings.
Interesting, though if that's the case I'm now even more confused about what it does...
The transform program that is loaded in the frame is 14 slots, though it's possible that there's a longer program that was loaded earlier during the startup sequence that I didn't capture in my pgraph trace. I suppose it is possible that such a shader could be set up to write to the c registers or it could be populating some output that isn't set by the 14 slot shader above, but the constants used by the 14 slot shader are all explicitly set, and the input values are also explicitly set via NV097_SET_VERTEX_DATA_ARRAY
invocations (v0, v1, and v2 are all populated just before the draw).
The shaders could be loaded independently. User can assign the starting slot dusting each load. The usages of these methods are vertex state shaders. These info are collected by reversing Xbox d3d RunVertexStateShader() And yes, the sample code from xdk for vertex state shader is mean to alter C registers. Vertex state shader is not associate to vertex stream.
Yeah, that's what I assumed as well. I did a more extensive trace (took a bit as I had to write a utility to convert from pgraph trace to nv2a-vsh asm) and it is explicitly loading a context setting program:
Shader at 0x3c (60)
MOV R2.xyzw, c[96]
MOV R3.xyzw, c[97]
MOV R4.xyzw, c[98]
MOV R5.xyzw, c[99]
DP4 c[104].y, R3, c[92]
MUL R8.xyzw, R4, v0.z
MUL R6.xyzw, R2, v0.x
DP4 c[104].x, R2, c[92]
DP4 c[105].x, R2, c[93]
DP4 c[106].x, R2, c[94]
DP4 c[107].x, R2, c[95]
DP4 c[105].y, R3, c[93]
DP4 c[106].y, R3, c[94]
DP4 c[107].y, R3, c[95]
DP4 c[104].z, R4, c[92]
DP4 c[105].z, R4, c[93]
DP4 c[106].z, R4, c[94]
DP4 c[107].z, R4, c[95]
DP4 c[104].w, R5, c[92]
DP4 c[105].w, R5, c[93]
DP4 c[106].w, R5, c[94]
DP4 c[107].w, R5, c[95]
MUL R7.xyzw, R3, v0.y
MUL R9.xyzw, R5, v0.w
DP4 c[100].x, R6, c[92]
DP4 c[101].x, R6, c[93]
DP4 c[102].x, R6, c[94]
DP4 c[103].x, R6, c[95]
DP4 c[100].y, R7, c[92]
DP4 c[101].y, R7, c[93]
DP4 c[102].y, R7, c[94]
DP4 c[103].y, R7, c[95]
DP4 c[100].z, R8, c[92]
DP4 c[101].z, R8, c[93]
DP4 c[102].z, R8, c[94]
DP4 c[103].z, R8, c[95]
DP4 c[100].w, R9, c[92]
DP4 c[101].w, R9, c[93]
DP4 c[102].w, R9, c[94]
DP4 c[103].w, R9, c[95]
DP4 c[96].x, R6, c[88]
DP4 c[97].x, R6, c[89]
DP4 c[98].x, R6, c[90]
DP4 c[99].x, R6, c[91]
DP4 c[96].y, R7, c[88]
DP4 c[97].y, R7, c[89]
DP4 c[98].y, R7, c[90]
DP4 c[99].y, R7, c[91]
DP4 c[96].z, R8, c[88]
DP4 c[97].z, R8, c[89]
DP4 c[98].z, R8, c[90]
DP4 c[99].z, R8, c[91]
DP4 c[96].w, R9, c[88]
DP4 c[97].w, R9, c[89]
DP4 c[98].w, R9, c[90]
DP4 c[99].w, R9, c[91]
Unfortunately this will be non-trivial to implement as we don't have a good way to emulate writable c-registers.
It looks like all of the important registers other than c[125] would be modified by this shader and thus may not match the values being used by xemu. The other constants are all set before the LAUNCH is invoked.
Damn you’re quick! Hand crafting a code to verify the result is the quickest way I guess.
Confirmed that implementing these two commands fixes the menu.
did you press the START and check the in game renderings? the in game renderings uses to have similar issues, should be fixed as well.
I've never actually played the game on HW before and am away from my Xbox for the next few days (I bought it specifically because it was broken in xemu and dumped it), but in-game looks reasonable to me at a glance.
Title
https://xemu.app/titles/5454007c/#Spy-vs-Spy
Bug Description
Note: This is an intentional dup of the generic #309 and #537 so I have a place to put my game-specific analysis.
The main menu in Spy Vs Spy renders with highly distorted geometry, most of the draws are clipped entirely due to the fact that the w component of the programmable vertex shader is very negative.
E.g., one of the screen space vertices:
{320.7315979004, 253.5145263672, 49013.19921875, -100978.4765625}
Expected Behavior
The menu should not be corrupted, and draws should generally be unclipped and projected properly.
xemu Version
Happens in 0.7.39 and has apparently been happening for a long time (or forever), at least since 0.6.2
System Information
CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz OS Platform: Linux OS Version: Ubuntu 21.10 Manufacturer: NVIDIA Corporation GPU Model: NVIDIA GeForce GTX 1070/PCIe/SSE2 Driver: 4.0.0 NVIDIA 470.129.06 Shader: 4.00 NVIDIA via Cg compiler
Additional Context
The game uses S32K mode for the vertex positions, which is not something I've come across before. I wrote a quick test to validate xemu's behavior and it seems to align with what I see in HW, so there's something else going on here.