mirror of
https://github.com/xemu-project/xemu.git
synced 2026-10-19 17:19:19 +02:00
nv2a: Fix R12 input in paired/multiout ops writing to oPos
Paired MAC+ILU and duplicate output instructions can write to oPos while reading from R12. This leads to a case where xemu's serialized emulation erroneously uses the output of a previous instruction when calculating the value of a later one. E.g., in ``` /* 0x00000000 0x0080201A 0xC4002868 0x7CB0E800 */ MAD oPos.xyz, R12.xyz, R1.x, C[1].xyz + MAD R11.xy, R12.xyz, R1.x, C[1].xyz ``` the value of oPos prior to the first instruction should be used for both MAD calculations. This could alternatively be fixed by writing oPos to a temp vector and deferring the output vector update until after the token is fully processed. Note that we always process output registers writes before temp register writes so the only interesting case should be the oPos/R12 alias. An op that writes to one of its temp inputs will always execute the non-modifying output register write before updating the temp register. [Tests](https://github.com/abaire/nxdk_pgraph_tests/blob/1745a45290a5d607f9582303d6882cbf62f003de/src/tests/vertex_shader_independence_tests.cpp#L125) [HW results](https://abaire.github.io/nxdk_pgraph_tests_golden_results/results/Vertex_shader_independence_tests/index.html)
This commit is contained in:
1 parent
4d311d6ee3
commit
0103939ba4
1 file changed
+32
-10
@@ -209,6 +209,7 @@ static bool ilu_force_scalar[] = {
|
||||
false,
|
||||
};
|
||||
|
||||
#define OUTPUT_REG_POS 0
|
||||
#define OUTPUT_REG_FOG 5
|
||||
|
||||
static const char* out_reg_name[] = {
|
||||
@@ -304,7 +305,6 @@ static MString *decode_opcode_input(const uint32_t *shader_token,
|
||||
|
||||
MString *ret_str = mstring_new();
|
||||
|
||||
|
||||
if (vsh_get_field(shader_token, neg_field) > 0) {
|
||||
mstring_append_fmt(ret_str, "-");
|
||||
}
|
||||
@@ -354,6 +354,7 @@ static MString *decode_opcode(const uint32_t *shader_token,
|
||||
MString *ret = mstring_new();
|
||||
int reg_num = vsh_get_field(shader_token, FLD_OUT_R);
|
||||
bool use_temp_var = false;
|
||||
bool use_temp_opos = false;
|
||||
|
||||
/* Test for paired opcodes (in other words : Are both <> NOP?) */
|
||||
if (out_mux == OMUX_MAC
|
||||
@@ -369,6 +370,8 @@ static MString *decode_opcode(const uint32_t *shader_token,
|
||||
reg_num = 1;
|
||||
}
|
||||
|
||||
const char *write_mask_str = NULL;
|
||||
|
||||
/* See if we must add a muxed opcode too: */
|
||||
if (vsh_get_field(shader_token, FLD_OUT_MUX) == out_mux
|
||||
/* Only if it's not masked away: */
|
||||
@@ -386,19 +389,23 @@ static MString *decode_opcode(const uint32_t *shader_token,
|
||||
shader_token, FLD_OUT_ADDRESS)));
|
||||
} else {
|
||||
int out_reg = vsh_get_field(shader_token, FLD_OUT_ADDRESS) & 0xF;
|
||||
mstring_append(ret,out_reg_name[out_reg]);
|
||||
if (out_reg == OUTPUT_REG_POS && (use_temp_var || mask)) {
|
||||
use_temp_opos = true;
|
||||
mstring_append(ret, "_temp_r12");
|
||||
} else {
|
||||
mstring_append(ret, out_reg_name[out_reg]);
|
||||
}
|
||||
write_fog_register = out_reg == OUTPUT_REG_FOG;
|
||||
}
|
||||
|
||||
int write_mask = vsh_get_field(shader_token, FLD_OUT_O_MASK);
|
||||
const char *write_mask_str = write_fog_register ? fog_mask_str[write_mask] : mask_str[write_mask];
|
||||
write_mask_str = write_fog_register ? fog_mask_str[write_mask] : mask_str[write_mask];
|
||||
mstring_append(ret, write_mask_str);
|
||||
mstring_append(ret, inputs);
|
||||
mstring_append(ret, ");\n");
|
||||
}
|
||||
|
||||
if (use_temp_var) {
|
||||
assert(suffix && "Temp var flagged on non-MAC instruction");
|
||||
*suffix = mstring_new();
|
||||
if (strcmp(opcode, mac_opcode[MAC_ARL]) == 0) {
|
||||
mstring_append_fmt(ret, " ARL(_temp_addr%s);\n", inputs);
|
||||
@@ -428,6 +435,16 @@ static MString *decode_opcode(const uint32_t *shader_token,
|
||||
}
|
||||
}
|
||||
|
||||
if (use_temp_opos) {
|
||||
if (!*suffix) {
|
||||
*suffix = mstring_new();
|
||||
}
|
||||
assert(write_mask_str && "Temp oPos flagged without a mask");
|
||||
const char *write_mask_components = write_mask_str + 1;
|
||||
mstring_append_fmt(*suffix, " oPos.%s = _temp_r12.%s;\n",
|
||||
write_mask_components, write_mask_components);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -451,7 +468,7 @@ static MString *decode_token(const uint32_t *shader_token)
|
||||
(vsh_get_field(shader_token, FLD_C_R_HIGH) << 2)
|
||||
| vsh_get_field(shader_token, FLD_C_R_LOW));
|
||||
|
||||
MString *mac_suffix = NULL;
|
||||
MString *suffix = NULL;
|
||||
if (mac != MAC_NOP) {
|
||||
MString *inputs_mac = mstring_new();
|
||||
if (mac_opcode_params[mac].A) {
|
||||
@@ -485,7 +502,7 @@ static MString *decode_token(const uint32_t *shader_token)
|
||||
vsh_get_field(shader_token, FLD_OUT_MAC_MASK),
|
||||
mac_opcode[mac],
|
||||
mstring_get_str(inputs_mac),
|
||||
&mac_suffix);
|
||||
&suffix);
|
||||
mstring_unref(inputs_mac);
|
||||
} else {
|
||||
ret = mstring_new();
|
||||
@@ -502,7 +519,7 @@ static MString *decode_token(const uint32_t *shader_token)
|
||||
vsh_get_field(shader_token, FLD_OUT_ILU_MASK),
|
||||
ilu_opcode[ilu],
|
||||
mstring_get_str(inputs_c),
|
||||
NULL);
|
||||
&suffix);
|
||||
|
||||
mstring_append(ret, mstring_get_str(ilu_op));
|
||||
|
||||
@@ -512,9 +529,9 @@ static MString *decode_token(const uint32_t *shader_token)
|
||||
|
||||
mstring_unref(input_c);
|
||||
|
||||
if (mac_suffix) {
|
||||
mstring_append(ret, mstring_get_str(mac_suffix));
|
||||
mstring_unref(mac_suffix);
|
||||
if (suffix) {
|
||||
mstring_append(ret, mstring_get_str(suffix));
|
||||
mstring_unref(suffix);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -543,6 +560,11 @@ static const char* vsh_header =
|
||||
"vec4 _temp_vec;\n"
|
||||
"int _temp_addr;\n"
|
||||
|
||||
/* Used to emulate concurrency of multi-output / paired instructions that
|
||||
* write to oPos and read from R12
|
||||
*/
|
||||
"vec4 _temp_r12;\n"
|
||||
|
||||
/* See:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb174703%28v=vs.85%29.aspx
|
||||
* https://www.opengl.org/registry/specs/NV/vertex_program1_1.txt
|
||||
|
||||
Reference in new issue
Block a user