Skip to content

Commit eaaddb2

Browse files
hzqstTheMostDiligent
authored andcommitted
Add specialization constant failure tests
1 parent d713c22 commit eaaddb2

2 files changed

Lines changed: 538 additions & 1 deletion

File tree

Tests/DiligentCoreAPITest/src/RenderStateCacheTest.cpp

Lines changed: 255 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2025 Diligent Graphics LLC
2+
* Copyright 2019-2026 Diligent Graphics LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1918,4 +1918,258 @@ TEST(RenderStateCacheTest, NonSeparableProgramBindingConflict)
19181918
ASSERT_EQ(pPSO->GetStatus(), PIPELINE_STATE_STATUS_READY);
19191919
}
19201920

1921+
1922+
void TestSpecializationConstantsCache()
1923+
{
1924+
auto* pEnv = GPUTestingEnvironment::GetInstance();
1925+
auto* pDevice = pEnv->GetDevice();
1926+
1927+
if (pDevice->GetDeviceInfo().Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED)
1928+
{
1929+
GTEST_SKIP() << "Specialization constants are not supported by this device";
1930+
}
1931+
1932+
GPUTestingEnvironment::ScopedReset AutoReset;
1933+
1934+
auto CreateSpecConstShaders = [&](IRenderStateCache* pCache,
1935+
RefCntAutoPtr<IShader>& pVS,
1936+
RefCntAutoPtr<IShader>& pPS,
1937+
bool PresentInCache) {
1938+
ShaderCreateInfo ShaderCI;
1939+
ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
1940+
ShaderCI.ShaderCompiler = pEnv->GetDefaultCompiler(ShaderCI.SourceLanguage);
1941+
1942+
{
1943+
ShaderCI.Desc = {"SpecConst Cache VS", SHADER_TYPE_VERTEX, true};
1944+
ShaderCI.EntryPoint = "main";
1945+
ShaderCI.Source = HLSL::DrawTest_ProceduralTriangleVS.c_str();
1946+
CreateShader(pCache, ShaderCI, PresentInCache, pVS);
1947+
ASSERT_TRUE(pVS);
1948+
}
1949+
1950+
{
1951+
ShaderCI.Desc = {"SpecConst Cache PS", SHADER_TYPE_PIXEL, true};
1952+
ShaderCI.EntryPoint = "main";
1953+
ShaderCI.Source = HLSL::DrawTest_PS.c_str();
1954+
CreateShader(pCache, ShaderCI, PresentInCache, pPS);
1955+
ASSERT_TRUE(pPS);
1956+
}
1957+
};
1958+
1959+
auto CreateSpecConstPSO = [&](IRenderStateCache* pCache,
1960+
bool PresentInCache,
1961+
IShader* pVS,
1962+
IShader* pPS,
1963+
const SpecializationConstant* pSpecConsts,
1964+
Uint32 NumSpecConsts,
1965+
RefCntAutoPtr<IPipelineState>& pPSO) {
1966+
auto* pSwapChain = pEnv->GetSwapChain();
1967+
1968+
GraphicsPipelineStateCreateInfo PsoCI;
1969+
PsoCI.PSODesc.Name = "Spec Const Cache Test";
1970+
1971+
PsoCI.pVS = pVS;
1972+
PsoCI.pPS = pPS;
1973+
1974+
PsoCI.GraphicsPipeline.NumRenderTargets = 1;
1975+
PsoCI.GraphicsPipeline.RTVFormats[0] = pSwapChain->GetDesc().ColorBufferFormat;
1976+
PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False;
1977+
1978+
PsoCI.pSpecializationConstants = pSpecConsts;
1979+
PsoCI.NumSpecializationConstants = NumSpecConsts;
1980+
1981+
if (pCache != nullptr)
1982+
{
1983+
bool PSOFound = pCache->CreateGraphicsPipelineState(PsoCI, &pPSO);
1984+
EXPECT_EQ(PSOFound, PresentInCache);
1985+
}
1986+
else
1987+
{
1988+
pDevice->CreateGraphicsPipelineState(PsoCI, &pPSO);
1989+
}
1990+
ASSERT_NE(pPSO, nullptr);
1991+
};
1992+
1993+
auto VerifySpecConstPSO = [&](IPipelineState* pPSO) {
1994+
auto* pCtx = pEnv->GetDeviceContext();
1995+
auto* pSwapChain = pEnv->GetSwapChain();
1996+
1997+
static FastRandFloat rnd{2, 0, 1};
1998+
const float ClearColor[] = {rnd(), rnd(), rnd(), rnd()};
1999+
RenderDrawCommandReference(pSwapChain, ClearColor);
2000+
2001+
ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()};
2002+
pCtx->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
2003+
pCtx->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
2004+
2005+
pCtx->SetPipelineState(pPSO);
2006+
pCtx->Draw({6, DRAW_FLAG_VERIFY_ALL});
2007+
2008+
pSwapChain->Present();
2009+
};
2010+
2011+
const float SpecConstValue = 1.0f;
2012+
SpecializationConstant SpecConsts[] = {
2013+
{"TestConstant", SHADER_TYPE_VERTEX, sizeof(SpecConstValue), &SpecConstValue},
2014+
};
2015+
2016+
// Create uncached reference PSO
2017+
RefCntAutoPtr<IShader> pUncachedVS, pUncachedPS;
2018+
CreateSpecConstShaders(nullptr, pUncachedVS, pUncachedPS, false);
2019+
ASSERT_NE(pUncachedVS, nullptr);
2020+
ASSERT_NE(pUncachedPS, nullptr);
2021+
2022+
RefCntAutoPtr<IPipelineState> pRefPSO;
2023+
CreateSpecConstPSO(nullptr, false, pUncachedVS, pUncachedPS, SpecConsts, _countof(SpecConsts), pRefPSO);
2024+
ASSERT_NE(pRefPSO, nullptr);
2025+
2026+
RefCntAutoPtr<IDataBlob> pData;
2027+
for (Uint32 pass = 0; pass < 3; ++pass)
2028+
{
2029+
// 0: empty cache
2030+
// 1: loaded cache
2031+
// 2: reloaded cache (loaded -> stored -> loaded)
2032+
2033+
auto pCache = CreateCache(pDevice, /*HotReload=*/false, pData);
2034+
ASSERT_TRUE(pCache);
2035+
2036+
RefCntAutoPtr<IShader> pVS, pPS;
2037+
CreateSpecConstShaders(pCache, pVS, pPS, pData != nullptr);
2038+
ASSERT_NE(pVS, nullptr);
2039+
ASSERT_NE(pPS, nullptr);
2040+
2041+
RefCntAutoPtr<IPipelineState> pPSO;
2042+
CreateSpecConstPSO(pCache, pData != nullptr, pVS, pPS, SpecConsts, _countof(SpecConsts), pPSO);
2043+
ASSERT_NE(pPSO, nullptr);
2044+
ASSERT_EQ(pPSO->GetStatus(), PIPELINE_STATE_STATUS_READY);
2045+
2046+
VerifySpecConstPSO(pPSO);
2047+
2048+
// Request the same PSO again - should be found in cache
2049+
{
2050+
RefCntAutoPtr<IPipelineState> pPSO2;
2051+
CreateSpecConstPSO(pCache, true, pVS, pPS, SpecConsts, _countof(SpecConsts), pPSO2);
2052+
EXPECT_EQ(pPSO, pPSO2);
2053+
}
2054+
2055+
pData.Release();
2056+
pCache->WriteToBlob(pass == 0 ? ContentVersion : ~0u, &pData);
2057+
}
2058+
}
2059+
2060+
TEST(RenderStateCacheTest, SpecializationConstants)
2061+
{
2062+
TestSpecializationConstantsCache();
2063+
}
2064+
2065+
2066+
TEST(RenderStateCacheTest, SpecializationConstants_DistinctEntries)
2067+
{
2068+
auto* pEnv = GPUTestingEnvironment::GetInstance();
2069+
auto* pDevice = pEnv->GetDevice();
2070+
2071+
if (pDevice->GetDeviceInfo().Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED)
2072+
{
2073+
GTEST_SKIP() << "Specialization constants are not supported by this device";
2074+
}
2075+
2076+
GPUTestingEnvironment::ScopedReset AutoReset;
2077+
2078+
auto* pSwapChain = pEnv->GetSwapChain();
2079+
2080+
const float ValueA = 1.0f;
2081+
SpecializationConstant SpecConstsA[] = {
2082+
{"TestConstant", SHADER_TYPE_VERTEX, sizeof(ValueA), &ValueA},
2083+
};
2084+
2085+
const float ValueB = 2.0f;
2086+
SpecializationConstant SpecConstsB[] = {
2087+
{"TestConstant", SHADER_TYPE_VERTEX, sizeof(ValueB), &ValueB},
2088+
};
2089+
2090+
RefCntAutoPtr<IDataBlob> pData;
2091+
for (Uint32 pass = 0; pass < 3; ++pass)
2092+
{
2093+
// 0: empty cache
2094+
// 1: loaded cache
2095+
// 2: reloaded cache (loaded -> stored -> loaded)
2096+
2097+
auto pCache = CreateCache(pDevice, /*HotReload=*/false, pData);
2098+
ASSERT_TRUE(pCache);
2099+
2100+
// Create shaders
2101+
ShaderCreateInfo ShaderCI;
2102+
ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
2103+
ShaderCI.ShaderCompiler = pEnv->GetDefaultCompiler(ShaderCI.SourceLanguage);
2104+
2105+
RefCntAutoPtr<IShader> pVS, pPS;
2106+
{
2107+
ShaderCI.Desc = {"SpecConst Distinct VS", SHADER_TYPE_VERTEX, true};
2108+
ShaderCI.EntryPoint = "main";
2109+
ShaderCI.Source = HLSL::DrawTest_ProceduralTriangleVS.c_str();
2110+
CreateShader(pCache, ShaderCI, pData != nullptr, pVS);
2111+
ASSERT_TRUE(pVS);
2112+
}
2113+
{
2114+
ShaderCI.Desc = {"SpecConst Distinct PS", SHADER_TYPE_PIXEL, true};
2115+
ShaderCI.EntryPoint = "main";
2116+
ShaderCI.Source = HLSL::DrawTest_PS.c_str();
2117+
CreateShader(pCache, ShaderCI, pData != nullptr, pPS);
2118+
ASSERT_TRUE(pPS);
2119+
}
2120+
2121+
auto CreatePSO = [&](const SpecializationConstant* pSpecConsts,
2122+
Uint32 NumSpecConsts,
2123+
bool PresentInCache) -> RefCntAutoPtr<IPipelineState> {
2124+
GraphicsPipelineStateCreateInfo PsoCI;
2125+
PsoCI.PSODesc.Name = "Spec Const Distinct Test";
2126+
2127+
PsoCI.pVS = pVS;
2128+
PsoCI.pPS = pPS;
2129+
2130+
PsoCI.GraphicsPipeline.NumRenderTargets = 1;
2131+
PsoCI.GraphicsPipeline.RTVFormats[0] = pSwapChain->GetDesc().ColorBufferFormat;
2132+
PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False;
2133+
2134+
PsoCI.pSpecializationConstants = pSpecConsts;
2135+
PsoCI.NumSpecializationConstants = NumSpecConsts;
2136+
2137+
RefCntAutoPtr<IPipelineState> pPSO;
2138+
bool PSOFound = pCache->CreateGraphicsPipelineState(PsoCI, &pPSO);
2139+
EXPECT_EQ(PSOFound, PresentInCache);
2140+
return pPSO;
2141+
};
2142+
2143+
// Create PSO with value A
2144+
auto pPSO_A = CreatePSO(SpecConstsA, _countof(SpecConstsA), pData != nullptr);
2145+
ASSERT_NE(pPSO_A, nullptr);
2146+
ASSERT_EQ(pPSO_A->GetStatus(), PIPELINE_STATE_STATUS_READY);
2147+
2148+
// Create PSO with value B (different value, same name)
2149+
auto pPSO_B = CreatePSO(SpecConstsB, _countof(SpecConstsB), pData != nullptr);
2150+
ASSERT_NE(pPSO_B, nullptr);
2151+
ASSERT_EQ(pPSO_B->GetStatus(), PIPELINE_STATE_STATUS_READY);
2152+
2153+
// PSOs must be different objects (different spec constant values => different hash)
2154+
EXPECT_NE(pPSO_A, pPSO_B);
2155+
2156+
// Request PSO A again - should find exact match in cache
2157+
auto pPSO_A2 = CreatePSO(SpecConstsA, _countof(SpecConstsA), true);
2158+
EXPECT_EQ(pPSO_A, pPSO_A2);
2159+
2160+
// Request PSO B again - should find exact match in cache
2161+
auto pPSO_B2 = CreatePSO(SpecConstsB, _countof(SpecConstsB), true);
2162+
EXPECT_EQ(pPSO_B, pPSO_B2);
2163+
2164+
// A PSO with no spec constants must also be distinct from both A and B
2165+
auto pPSO_None = CreatePSO(nullptr, 0, pData != nullptr);
2166+
ASSERT_NE(pPSO_None, nullptr);
2167+
EXPECT_NE(pPSO_None, pPSO_A);
2168+
EXPECT_NE(pPSO_None, pPSO_B);
2169+
2170+
pData.Release();
2171+
pCache->WriteToBlob(pass == 0 ? ContentVersion : ~0u, &pData);
2172+
}
2173+
}
2174+
19212175
} // namespace

0 commit comments

Comments
 (0)