@@ -839,6 +839,180 @@ TEST_F(InlineConstants, TwoResourceSignatures)
839839 TestSignatures (2 );
840840}
841841
842+ // Test for "Inline Constants Cross-Signature Commit Inconsistency" fix.
843+ //
844+ // This test verifies that inline constants work correctly when an SRB created
845+ // from one signature is used with a PSO created from a different but compatible
846+ // signature instance.
847+ //
848+ // Bug scenario:
849+ // 1. PSO is created with Signature A
850+ // 2. SRB is created from Signature B (compatible but different instance)
851+ // 3. Inline constants are set through SRB
852+ // 4. BindResources() binds buffers from SRB cache (pointing to Signature B's buffers)
853+ // 5. BUG (fixed): UpdateInlineConstantBuffers() was updating Signature A's buffers
854+ // instead of the buffers from SRB cache
855+ //
856+ // The fix ensures that UpdateInlineConstantBuffers() updates the buffer from
857+ // SRB cache - the same buffer that was bound during BindResources().
858+ //
859+ // In Vulkan, only the first inline constant uses push constants (always works).
860+ // The second inline constant uses buffer-emulated path where this bug manifested.
861+ // Therefore, this test uses TWO inline constant buffers to ensure we test both paths.
862+ TEST_F (InlineConstants, CrossSignatureSRB)
863+ {
864+ GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance ();
865+ IRenderDevice* pDevice = pEnv->GetDevice ();
866+ IDeviceContext* pContext = pEnv->GetDeviceContext ();
867+ ISwapChain* pSwapChain = pEnv->GetSwapChain ();
868+
869+ // Test all variable types to ensure complete coverage
870+ for (Uint32 pos_type = 0 ; pos_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++pos_type)
871+ {
872+ for (Uint32 col_type = 0 ; col_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++col_type)
873+ {
874+ const float ClearColor[] = {sm_Rnd (), sm_Rnd (), sm_Rnd (), sm_Rnd ()};
875+ RenderDrawCommandReference (pSwapChain, ClearColor);
876+
877+ SHADER_RESOURCE_VARIABLE_TYPE PosType = static_cast <SHADER_RESOURCE_VARIABLE_TYPE>(pos_type);
878+ SHADER_RESOURCE_VARIABLE_TYPE ColType = static_cast <SHADER_RESOURCE_VARIABLE_TYPE>(col_type);
879+
880+ // Create TWO IDENTICAL but SEPARATE signature instances
881+ // This is the key to reproducing the cross-signature issue:
882+ // - pSign1 and pSign2 are compatible (same resources)
883+ // - But they are different instances with separate internal buffers
884+ RefCntAutoPtr<IPipelineResourceSignature> pSign1, pSign2;
885+
886+ PipelineResourceSignatureDescX SignDesc{" Cross-Signature Test" };
887+ SignDesc
888+ // First inline constant (Vulkan: push constants path)
889+ .AddResource (SHADER_TYPE_VERTEX, " cbInlinePositions" , kNumPosConstants ,
890+ SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType,
891+ PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)
892+ // Second inline constant (Vulkan: buffer-emulated path - where the bug was)
893+ .AddResource (SHADER_TYPE_VS_PS, " cbInlineColors" , kNumColConstants ,
894+ SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, ColType,
895+ PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS);
896+ SignDesc.SetSRBAllocationGranularity (4 );
897+
898+ // Create first signature instance
899+ pDevice->CreatePipelineResourceSignature (SignDesc, &pSign1);
900+ ASSERT_TRUE (pSign1);
901+
902+ // Create second signature instance with identical descriptor
903+ // This creates a separate instance with its own internal buffers
904+ pDevice->CreatePipelineResourceSignature (SignDesc, &pSign2);
905+ ASSERT_TRUE (pSign2);
906+
907+ // IMPORTANT: pSign1 and pSign2 are compatible but have separate buffer allocations
908+ // Using an SRB from pSign2 with a PSO from pSign1 tests the cross-signature path
909+
910+ // Create PSO using signature 1
911+ GraphicsPipelineStateCreateInfoX PsoCI{" Cross-Signature Test" };
912+ PsoCI
913+ .AddRenderTarget (pSwapChain->GetDesc ().ColorBufferFormat )
914+ .SetPrimitiveTopology (PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
915+ .AddShader (sm_Res.pVS )
916+ .AddShader (sm_Res.pPS )
917+ .AddSignature (pSign1); // PSO uses signature 1
918+ PsoCI.GraphicsPipeline .DepthStencilDesc .DepthEnable = False;
919+
920+ RefCntAutoPtr<IPipelineState> pPSO;
921+ pDevice->CreateGraphicsPipelineState (PsoCI, &pPSO);
922+ ASSERT_TRUE (pPSO);
923+
924+ // For STATIC variables, set on signature 2 (which will be used for SRB)
925+ if (PosType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
926+ {
927+ IShaderResourceVariable* pVar = pSign2->GetStaticVariableByName (SHADER_TYPE_VERTEX, " cbInlinePositions" );
928+ ASSERT_TRUE (pVar);
929+ pVar->SetInlineConstants (g_Positions, 0 , kNumPosConstants );
930+ }
931+
932+ if (ColType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
933+ {
934+ IShaderResourceVariable* pVar = pSign2->GetStaticVariableByName (SHADER_TYPE_VERTEX, " cbInlineColors" );
935+ ASSERT_TRUE (pVar);
936+ pVar->SetInlineConstants (g_Colors, 0 , kNumColConstants );
937+ }
938+
939+ // Create SRB from signature 2 (DIFFERENT from PSO's signature!)
940+ // This is the critical part: SRB is from pSign2, PSO is from pSign1
941+ RefCntAutoPtr<IShaderResourceBinding> pSRB;
942+ pSign2->CreateShaderResourceBinding (&pSRB, true );
943+ ASSERT_TRUE (pSRB);
944+
945+ // Verify the SRB is compatible with PSO (they should be, since signatures are identical)
946+ // Note: IsCompatibleWith checks signature compatibility
947+ EXPECT_TRUE (pPSO->IsCompatibleWith (pSign2));
948+
949+ IShaderResourceVariable* pPosVar = nullptr ;
950+ IShaderResourceVariable* pColVarVS = nullptr ;
951+ IShaderResourceVariable* pColVarPS = nullptr ;
952+
953+ if (PosType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
954+ {
955+ pPosVar = pSRB->GetVariableByName (SHADER_TYPE_VERTEX, " cbInlinePositions" );
956+ ASSERT_TRUE (pPosVar);
957+ }
958+
959+ if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
960+ {
961+ pColVarVS = pSRB->GetVariableByName (SHADER_TYPE_VERTEX, " cbInlineColors" );
962+ ASSERT_TRUE (pColVarVS);
963+ pColVarPS = pSRB->GetVariableByName (SHADER_TYPE_PIXEL, " cbInlineColors" );
964+ ASSERT_TRUE (pColVarPS);
965+ }
966+
967+ ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV ()};
968+ pContext->SetRenderTargets (1 , pRTVs, nullptr , RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
969+ pContext->ClearRenderTarget (pRTVs[0 ], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
970+
971+ // Set first half of color constants BEFORE CommitShaderResources
972+ if (pColVarVS != nullptr )
973+ {
974+ pColVarVS->SetInlineConstants (g_Colors, 0 , kNumColConstants / 2 );
975+ }
976+
977+ pContext->SetPipelineState (pPSO);
978+ pContext->CommitShaderResources (pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
979+
980+ // Set second half of color constants AFTER CommitShaderResources but BEFORE Draw
981+ // This is the specific timing that triggered the original bug:
982+ // - CommitShaderResources binds buffers from SRB cache
983+ // - SetInlineConstants writes to CPU staging
984+ // - Draw triggers UpdateInlineConstantBuffers which must update the CORRECT buffer
985+ if (pColVarPS != nullptr )
986+ {
987+ pColVarPS->SetInlineConstants (g_Colors[0 ].Data () + kNumColConstants / 2 ,
988+ kNumColConstants / 2 , kNumColConstants / 2 );
989+ }
990+
991+ if (pPosVar == nullptr )
992+ {
993+ // Draw both triangles as positions are static
994+ pContext->Draw ({6 , DRAW_FLAG_VERIFY_ALL});
995+ }
996+ else
997+ {
998+ // Draw first triangle
999+ pPosVar->SetInlineConstants (g_Positions, 0 , kNumPosConstants / 2 );
1000+ pContext->Draw ({3 , DRAW_FLAG_VERIFY_ALL});
1001+
1002+ // Draw second triangle - also tests update after CommitShaderResources
1003+ pPosVar->SetInlineConstants (g_Positions[0 ].Data () + kNumPosConstants / 2 , 0 , kNumPosConstants / 2 );
1004+ pContext->Draw ({3 , DRAW_FLAG_VERIFY_ALL});
1005+ }
1006+
1007+ Present ();
1008+
1009+ std::cout << TestingEnvironment::GetCurrentTestStatusString () << ' '
1010+ << " Pos " << GetShaderVariableTypeLiteralName (PosType) << ' ,'
1011+ << " Col " << GetShaderVariableTypeLiteralName (ColType) << std::endl;
1012+ }
1013+ }
1014+ }
1015+
8421016constexpr Uint32 kCacheContentVersion = 7 ;
8431017
8441018RefCntAutoPtr<IRenderStateCache> CreateCache (IRenderDevice* pDevice,
0 commit comments