Skip to content

Commit 7f26f72

Browse files
committed
D3D11: Migrate consumers from regs_generation to dirty[] groups
Replace the monolithic regs_generation counter with fine-grained dirty group generation counters throughout the HLE rendering pipeline: - XbPixelShaderCompiler: Regs SRV upload still uses dirty[PGRAPH] (needs any-register sensitivity). Aux CB rebuild narrowed to check only SHADER + TEXTURE + BLEND + RASTERIZER groups. - PixelShaderCache: PS JIT key build narrowed to the same 4 groups, skipping rebuilds triggered by unrelated register writes (e.g. VS constants, surface state). - XbVertexShader: Migrated to dirty[PROGRAM] generation counter with per-consumer s_Last*Gen tracking (replacing boolean clear pattern). - HostRender/HostSync/Backend_D3D11: Updated remaining consumers. Assign NV2A_DIRTY_SHADER to NV097_SET_SPECULAR_ENABLE (was NONE) since the aux CB and PS JIT read CSV0_C.SPECULAR_ENABLE for final combiner synthesis. Remove regs_generation field from PGRAPHState entirely.
1 parent c53a488 commit 7f26f72

10 files changed

Lines changed: 93 additions & 88 deletions

doc/pgraph_migration_status.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,6 @@ Draw callbacks registered in XbPushBuffer.cpp `CxbxInitD3D11Renderer()`:
107107

108108
- Light enable mask: `pg->regs[NV_PGRAPH_CSV0_D / 4] & NV_PGRAPH_CSV0_D_LIGHTS` (2 bits/light)
109109
- NV2A type mapping: 1(INFINITE)→3(DIRECTIONAL), 2(LOCAL)→1(POINT), 3(SPOT)→2(SPOT)
110-
- Colors from `pg->ltctxb[]` (pre-multiplied by material by Xbox D3D runtime)
110+
- Colors from `pg->xf.ltctxb[]` (pre-multiplied by material by Xbox D3D runtime)
111111
- Material forced to white — ltctxb already contains light×material product
112-
- Scene ambient from `pg->ltctxa[FR_AMB]` / `pg->ltctxa[BR_AMB]`
112+
- Scene ambient from `pg->xf.ltctxa[FR_AMB]` / `pg->xf.ltctxa[BR_AMB]`

src/core/hle/D3D8/Rendering/Backend/Backend_D3D11.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ ID3D11ShaderResourceView *g_pD3D11PGRegsSRV = nullptr; // SRV for g_PG
182182
bool g_bUseVSInterpreter = true; // default on — ubershader path
183183
ID3D11VertexShader *g_pD3D11VSInterpreterVS = nullptr;
184184
ID3DBlob *g_pD3D11VSInterpreterBytecode = nullptr; // kept for input layout creation
185-
ID3D11Buffer *g_pD3D11XFPRBuf = nullptr; // XFPR (Transform Program RAM) structured buffer — pg->program_data[]
185+
ID3D11Buffer *g_pD3D11XFPRBuf = nullptr; // XFPR (Transform Program RAM) structured buffer — pg->xf.xfpr[]
186186
ID3D11ShaderResourceView *g_pD3D11XFPRSRV = nullptr; // SRV for g_XFPR : register(t5)
187187

188188
// ******************************************************************

src/core/hle/D3D8/Rendering/Backend/Backend_D3D11_Internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ extern ID3D11ShaderResourceView *g_pD3D11PGRegsSRV; // SRV for g_PGR
130130
extern bool g_bUseVSInterpreter;
131131
extern ID3D11VertexShader *g_pD3D11VSInterpreterVS;
132132
extern ID3DBlob *g_pD3D11VSInterpreterBytecode;
133-
extern ID3D11Buffer *g_pD3D11XFPRBuf; // XFPR (Transform Program RAM) StructuredBuffer — pg->program_data[]
133+
extern ID3D11Buffer *g_pD3D11XFPRBuf; // XFPR (Transform Program RAM) StructuredBuffer — pg->xf.xfpr[]
134134
extern ID3D11ShaderResourceView *g_pD3D11XFPRSRV; // SRV for g_XFPR : register(t5)
135135

136136
// VS interpreter instruction field constants — shared with HLSL.

src/core/hle/D3D8/Rendering/Backend/Backend_D3D11_State.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -522,10 +522,9 @@ void CxbxD3D11UpdateViewportFromPGRAPH(PGRAPHState *pg)
522522
// The calculation is cheap (a few floats + two D3D11 calls).
523523

524524
// Read viewport offset and scale from XFCTX constants
525-
float vpoff[4], vpscl[4];
526-
for (int i = 0; i < 4; i++) {
527-
std::memcpy(&vpoff[i], &pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][i], sizeof(float));
528-
std::memcpy(&vpscl[i], &pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][i], sizeof(float));
525+
float vpscl[2];
526+
for (int i = 0; i < 2; i++) {
527+
std::memcpy(&vpscl[i], &pg->xf.xfctx[NV_IGRAPH_XF_XFCTX_VPSCL][i], sizeof(float));
529528
}
530529

531530
// If the viewport scale constants are zero, PGRAPH hasn't been programmed

src/core/hle/D3D8/Rendering/Backend/Shading/PixelShaderCache.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -875,13 +875,16 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
875875

876876
PGRAPHState* pg = &g_NV2A->GetDeviceState()->pgraph;
877877

878-
// Fast path: if PGRAPH registers haven't changed since last call,
879-
// the combiner topology is identical — return cached result directly
880-
// without rebuilding the key or hashing.
878+
// Fast path: if none of the relevant dirty groups changed since last
879+
// call, the combiner topology is identical — return cached result
880+
// directly without rebuilding the key or hashing.
881+
// PS JIT reads SHADER, TEXTURE, BLEND, and RASTERIZER registers.
881882
static uint32_t s_LastPSRegsGen = ~0u;
882883
static ID3D11PixelShader* s_LastPSResult = nullptr;
883884
static PSJITKey s_LastKey = {};
884-
if (pg->regs_generation == s_LastPSRegsGen)
885+
uint32_t psRegsGen = pg->dirty[NV2A_DIRTY_SHADER] + pg->dirty[NV2A_DIRTY_TEXTURE]
886+
+ pg->dirty[NV2A_DIRTY_BLEND] + pg->dirty[NV2A_DIRTY_RASTERIZER];
887+
if (psRegsGen == s_LastPSRegsGen)
885888
return s_LastPSResult;
886889

887890
// Capture current state — read directly from PGRAPH registers
@@ -926,10 +929,10 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
926929
key.alphaKill[3] = aux.AlphaKill.w;
927930

928931
// Second fast path: if the key matches the last one (combiner state unchanged
929-
// despite regs_generation bumping from non-combiner register writes like VS
930-
// constants), skip the expensive hash + mutex + map lookup.
932+
// despite dirty groups bumping from non-combiner register writes),
933+
// skip the expensive hash + mutex + map lookup.
931934
if (memcmp(&key, &s_LastKey, sizeof(PSJITKey)) == 0) {
932-
s_LastPSRegsGen = pg->regs_generation;
935+
s_LastPSRegsGen = psRegsGen;
933936
return s_LastPSResult;
934937
}
935938

@@ -940,7 +943,7 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
940943
auto it = g_PSJITCache.find(hash);
941944
if (it != g_PSJITCache.end()) {
942945
s_LastKey = key;
943-
s_LastPSRegsGen = pg->regs_generation;
946+
s_LastPSRegsGen = psRegsGen;
944947
s_LastPSResult = it->second.pPS;
945948
return s_LastPSResult; // nullptr = known failure
946949
}
@@ -963,7 +966,7 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
963966
std::lock_guard<std::mutex> lock(g_PSJITMutex);
964967
g_PSJITCache[hash] = { pPS };
965968
s_LastKey = key;
966-
s_LastPSRegsGen = pg->regs_generation;
969+
s_LastPSRegsGen = psRegsGen;
967970
s_LastPSResult = pPS;
968971
return pPS;
969972
}
@@ -993,7 +996,7 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
993996
std::lock_guard<std::mutex> lock(g_PSJITMutex);
994997
g_PSJITCache[hash] = { nullptr };
995998
s_LastKey = key;
996-
s_LastPSRegsGen = pg->regs_generation;
999+
s_LastPSRegsGen = psRegsGen;
9971000
s_LastPSResult = nullptr;
9981001
return nullptr;
9991002
}
@@ -1009,7 +1012,7 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
10091012
std::lock_guard<std::mutex> lock(g_PSJITMutex);
10101013
g_PSJITCache[hash] = { nullptr };
10111014
s_LastKey = key;
1012-
s_LastPSRegsGen = pg->regs_generation;
1015+
s_LastPSRegsGen = psRegsGen;
10131016
s_LastPSResult = nullptr;
10141017
return nullptr;
10151018
}
@@ -1024,7 +1027,7 @@ ID3D11PixelShader* PixelShaderCache::GetShader(ID3D11Device* pDevice)
10241027
std::lock_guard<std::mutex> lock(g_PSJITMutex);
10251028
g_PSJITCache[hash] = { pPS };
10261029
s_LastKey = key;
1027-
s_LastPSRegsGen = pg->regs_generation;
1030+
s_LastPSRegsGen = psRegsGen;
10281031
s_LastPSResult = pPS;
10291032
return pPS;
10301033
}

src/core/hle/D3D8/Rendering/HostRender.cpp

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ void CxbxUpdateHostViewPortOffsetAndScaleConstants()
528528
// With draws going through pfifo, the PGRAPH values ARE the source of
529529
// truth — they are written before the draw in the push buffer and
530530
// processed sequentially by the puller. CxbxUpdateHostVertexShaderConstants
531-
// already uploads them from pg->vsh_constants[58/59].
531+
// already uploads them from pg->xf.xfctx[58/59].
532532
//
533533
// TODO: Re-enable this overwrite if HLE draw patches are restored.
534534
// Test Case: GTA III, Soldier of Fortune II (needed when HLE draws are active)
@@ -673,7 +673,7 @@ void UpdateFixedFunctionVertexShaderState()
673673
// Helper: direct-copy a 4x4 matrix from vsh_constants[base..base+3] — NO transpose
674674
auto ReadXFCTXMatrix = [&](D3DXMATRIX* pDst, int base) {
675675
for (int row = 0; row < 4; row++) {
676-
std::memcpy(&pDst->m[row][0], &pg->vsh_constants[base + row][0], 16);
676+
std::memcpy(&pDst->m[row][0], &pg->xf.xfctx[base + row][0], 16);
677677
}
678678
};
679679

@@ -685,7 +685,7 @@ void UpdateFixedFunctionVertexShaderState()
685685
// Read viewport offset from PGRAPH XFCTX (half-pixel bias applied by Xbox runtime)
686686
float vpoff[4];
687687
for (int i = 0; i < 4; i++) {
688-
std::memcpy(&vpoff[i], &pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][i], sizeof(float));
688+
std::memcpy(&vpoff[i], &pg->xf.xfctx[NV_IGRAPH_XF_XFCTX_VPOFF][i], sizeof(float));
689689
}
690690

691691
// NV2A FF pipeline: CMAT × position → screen-space coordinates (viewport baked in).
@@ -963,68 +963,68 @@ void UpdateFixedFunctionVertexShaderState()
963963
// Diffuse color from ltctxb (3 floats stored as uint32_t bit patterns)
964964
int base = NV_IGRAPH_XF_LTCTXB_L0_DIF + (int)i * 6;
965965
pShaderLight->Diffuse = D3DXVECTOR4(
966-
AsFloat(pg->ltctxb[base][0]),
967-
AsFloat(pg->ltctxb[base][1]),
968-
AsFloat(pg->ltctxb[base][2]),
966+
AsFloat(pg->xf.ltctxb[base][0]),
967+
AsFloat(pg->xf.ltctxb[base][1]),
968+
AsFloat(pg->xf.ltctxb[base][2]),
969969
1.0f);
970970

971971
// Specular color
972972
bool SpecularEnable = (csv0c & NV_PGRAPH_CSV0_C_SPECULAR_ENABLE) != 0;
973973
base = NV_IGRAPH_XF_LTCTXB_L0_SPC + (int)i * 6;
974974
if (SpecularEnable) {
975975
pShaderLight->Specular = D3DXVECTOR4(
976-
AsFloat(pg->ltctxb[base][0]),
977-
AsFloat(pg->ltctxb[base][1]),
978-
AsFloat(pg->ltctxb[base][2]),
976+
AsFloat(pg->xf.ltctxb[base][0]),
977+
AsFloat(pg->xf.ltctxb[base][1]),
978+
AsFloat(pg->xf.ltctxb[base][2]),
979979
1.0f);
980980
} else {
981981
pShaderLight->Specular = D3DXVECTOR4(0, 0, 0, 0);
982982
}
983983

984984
// Accumulate per-light ambient
985985
base = NV_IGRAPH_XF_LTCTXB_L0_AMB + (int)i * 6;
986-
LightAmbient.x += AsFloat(pg->ltctxb[base][0]);
987-
LightAmbient.y += AsFloat(pg->ltctxb[base][1]);
988-
LightAmbient.z += AsFloat(pg->ltctxb[base][2]);
986+
LightAmbient.x += AsFloat(pg->xf.ltctxb[base][0]);
987+
LightAmbient.y += AsFloat(pg->xf.ltctxb[base][1]);
988+
LightAmbient.z += AsFloat(pg->xf.ltctxb[base][2]);
989989

990990
// Direction (for directional lights — already in view-space, normalized)
991991
pShaderLight->DirectionVN = D3DXVECTOR3(
992-
pg->light_infinite_direction[i][0],
993-
pg->light_infinite_direction[i][1],
994-
pg->light_infinite_direction[i][2]);
992+
pg->light[i].infinite_direction[0],
993+
pg->light[i].infinite_direction[1],
994+
pg->light[i].infinite_direction[2]);
995995

996996
// Position (for point/spot lights — already in view-space)
997997
pShaderLight->PositionV = D3DXVECTOR3(
998-
pg->light_local_position[i][0],
999-
pg->light_local_position[i][1],
1000-
pg->light_local_position[i][2]);
998+
pg->light[i].local_position[0],
999+
pg->light[i].local_position[1],
1000+
pg->light[i].local_position[2]);
10011001

10021002
// Attenuation
10031003
pShaderLight->Attenuation = D3DXVECTOR3(
1004-
pg->light_local_attenuation[i][0],
1005-
pg->light_local_attenuation[i][1],
1006-
pg->light_local_attenuation[i][2]);
1004+
pg->light[i].local_attenuation[0],
1005+
pg->light[i].local_attenuation[1],
1006+
pg->light[i].local_attenuation[2]);
10071007

10081008
// Range (stored in ltc1)
1009-
pShaderLight->Range = AsFloat(pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + i][0]);
1009+
pShaderLight->Range = AsFloat(pg->xf.ltc1[NV_IGRAPH_XF_LTC1_r0 + i][0]);
10101010

10111011
// Spot parameters from ltctxa
10121012
int spotBase = NV_IGRAPH_XF_LTCTXA_L0_K + (int)i * 2;
1013-
pShaderLight->Falloff = AsFloat(pg->ltctxa[spotBase][2]); // falloff stored in K[2]
1014-
pShaderLight->CosHalfPhi = AsFloat(pg->ltctxa[spotBase][0]);
1015-
pShaderLight->SpotIntensityDivisor = AsFloat(pg->ltctxa[spotBase][1]);
1013+
pShaderLight->Falloff = AsFloat(pg->xf.ltctxa[spotBase][2]); // falloff stored in K[2]
1014+
pShaderLight->CosHalfPhi = AsFloat(pg->xf.ltctxa[spotBase][0]);
1015+
pShaderLight->SpotIntensityDivisor = AsFloat(pg->xf.ltctxa[spotBase][1]);
10161016
}
10171017

10181018
// Scene ambient from PGRAPH ltctxa[FR_AMB] (3 floats)
10191019
D3DXVECTOR4 SceneAmbient(
1020-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][0]),
1021-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][1]),
1022-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][2]),
1020+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][0]),
1021+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][1]),
1022+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][2]),
10231023
0.f);
10241024
D3DXVECTOR4 BackSceneAmbient(
1025-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][0]),
1026-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][1]),
1027-
AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][2]),
1025+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][0]),
1026+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][1]),
1027+
AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_BR_AMB][2]),
10281028
0.f);
10291029

10301030
ffShaderState.TotalLightsAmbient.Front = (D3DXVECTOR3)(LightAmbient + SceneAmbient);
@@ -1034,8 +1034,8 @@ void UpdateFixedFunctionVertexShaderState()
10341034
// The shader computes (material * light), so white material preserves the pre-multiplied values.
10351035
// Emission is already baked into the scene ambient register (FR_AMB/BR_AMB) by the Xbox D3D runtime.
10361036
// Material alpha comes from NV097_SET_MATERIAL_ALPHA → ltctxa[CM_COL][3].
1037-
float materialAlpha = AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_CM_COL][3]);
1038-
float backMaterialAlpha = AsFloat(pg->ltctxa[NV_IGRAPH_XF_LTCTXA_BCM_COL][3]);
1037+
float materialAlpha = AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_CM_COL][3]);
1038+
float backMaterialAlpha = AsFloat(pg->xf.ltctxa[NV_IGRAPH_XF_LTCTXA_BCM_COL][3]);
10391039

10401040
ffShaderState.Materials[0].Diffuse = D3DXVECTOR4(1, 1, 1, materialAlpha);
10411041
ffShaderState.Materials[0].Ambient = D3DXVECTOR4(1, 1, 1, 1);
@@ -1045,17 +1045,17 @@ void UpdateFixedFunctionVertexShaderState()
10451045
// Reconstruct specular power from NV2A's 6 polynomial coefficients (LTC1).
10461046
// Front specular params: ltc1[l0][0..3] + ltc1[l0+1][0..1]
10471047
float frontParams[6];
1048-
for (int j = 0; j < 4; j++) frontParams[j] = AsFloat(pg->ltc1[NV_IGRAPH_XF_LTC1_l0][j]);
1049-
for (int j = 0; j < 2; j++) frontParams[4 + j] = AsFloat(pg->ltc1[NV_IGRAPH_XF_LTC1_l0 + 1][j]);
1048+
for (int j = 0; j < 4; j++) frontParams[j] = AsFloat(pg->xf.ltc1[NV_IGRAPH_XF_LTC1_l0][j]);
1049+
for (int j = 0; j < 2; j++) frontParams[4 + j] = AsFloat(pg->xf.ltc1[NV_IGRAPH_XF_LTC1_l0 + 1][j]);
10501050
ffShaderState.Materials[0].Power = ReconstructSpecularPower(frontParams);
10511051

10521052
ffShaderState.Materials[1] = ffShaderState.Materials[0]; // back material (start from front)
10531053
ffShaderState.Materials[1].Diffuse.w = backMaterialAlpha;
10541054

10551055
// Back specular params: ltc1[Bl0][0..3] + ltc1[Bl0+1][0..1]
10561056
float backParams[6];
1057-
for (int j = 0; j < 4; j++) backParams[j] = AsFloat(pg->ltc1[NV_IGRAPH_XF_LTC1_Bl0][j]);
1058-
for (int j = 0; j < 2; j++) backParams[4 + j] = AsFloat(pg->ltc1[NV_IGRAPH_XF_LTC1_Bl0 + 1][j]);
1057+
for (int j = 0; j < 4; j++) backParams[j] = AsFloat(pg->xf.ltc1[NV_IGRAPH_XF_LTC1_Bl0][j]);
1058+
for (int j = 0; j < 2; j++) backParams[4 + j] = AsFloat(pg->xf.ltc1[NV_IGRAPH_XF_LTC1_Bl0 + 1][j]);
10591059
ffShaderState.Materials[1].Power = ReconstructSpecularPower(backParams);
10601060
}
10611061

src/core/hle/D3D8/Rendering/HostSync.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,10 +727,10 @@ void CxbxUpdateHostVertexShaderConstants()
727727
isXboxConstants = false;
728728
}
729729
else {
730-
auto constant_floats = (float*)pg->vsh_constants;
730+
auto constant_floats = (float*)pg->xf.xfctx;
731731

732732
if (isXboxConstants) {
733-
CxbxUpdateDirtyVertexShaderConstants(constant_floats, pg->vsh_constants_dirty);
733+
CxbxUpdateDirtyVertexShaderConstants(constant_floats, pg->xf.xfctx_dirty);
734734
}
735735
else {
736736
CxbxSetVertexShaderConstantF(0, constant_floats, X_D3DVS_CONSTREG_COUNT);

src/core/hle/D3D8/XbPixelShaderCompiler.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,14 @@ void CxbxD3D11UploadRCInterpreterState()
268268
PGRAPHState *pg = &g_NV2A->GetDeviceState()->pgraph;
269269

270270
// --- Upload raw PGRAPH regs[] to the StructuredBuffer<uint> SRV ---
271-
// Only re-upload when regs actually changed (generation counter bumped
271+
// Only re-upload when regs actually changed (dirty generation bumped
272272
// by nv097_dispatch_method on any register write).
273273
// NOTE: Both JIT and interpreter shaders read dynamic constants (C0/C1,
274274
// fog color, bump matrices) from this SRV at runtime, so upload is required.
275275
{
276276
static uint32_t s_LastRegsGeneration = ~0u;
277-
if (pg->regs_generation != s_LastRegsGeneration) {
278-
s_LastRegsGeneration = pg->regs_generation;
277+
if (pg->dirty[NV2A_DIRTY_PGRAPH] != s_LastRegsGeneration) {
278+
s_LastRegsGeneration = pg->dirty[NV2A_DIRTY_PGRAPH];
279279
CxbxD3D11UpdateDynamicBuffer(g_pD3D11PGRegsBuf, pg->regs, sizeof(pg->regs));
280280
}
281281
}
@@ -289,12 +289,15 @@ void CxbxD3D11UploadRCInterpreterState()
289289
}
290290

291291
// --- Build the auxiliary cbuffer (software-computed fields only) ---
292-
// Skip rebuild if regs_generation hasn't changed (aux depends only on regs[])
292+
// Skip rebuild if none of the relevant dirty groups changed.
293+
// Aux CB reads only SHADER, TEXTURE, BLEND, and RASTERIZER registers.
293294
{
294295
static uint32_t s_LastAuxGeneration = ~0u;
295-
if (pg->regs_generation == s_LastAuxGeneration)
296+
uint32_t auxGen = pg->dirty[NV2A_DIRTY_SHADER] + pg->dirty[NV2A_DIRTY_TEXTURE]
297+
+ pg->dirty[NV2A_DIRTY_BLEND] + pg->dirty[NV2A_DIRTY_RASTERIZER];
298+
if (auxGen == s_LastAuxGeneration)
296299
return; // Aux CB and regs SRV are still current
297-
s_LastAuxGeneration = pg->regs_generation;
300+
s_LastAuxGeneration = auxGen;
298301
}
299302
PSAuxCBLayout aux = {};
300303

0 commit comments

Comments
 (0)