Skip to content

Commit 92020eb

Browse files
committed
Add ValidateSpecializationConstantsTest
1 parent 3888351 commit 92020eb

2 files changed

Lines changed: 255 additions & 1 deletion

File tree

Graphics/GraphicsEngine/include/PipelineStateBase.hpp

Lines changed: 7 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
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -110,6 +110,12 @@ void ValidatePipelineResourceCompatibility(const PipelineResourceDesc& ResDesc,
110110
const char* ShaderName,
111111
const char* SignatureName) noexcept(false);
112112

113+
/// Validates specialization constants in PSO create info and throws an exception in case of an error.
114+
void ValidateSpecializationConstants(const PipelineStateDesc& PSODesc,
115+
const DeviceFeatures& Features,
116+
Uint32 NumSpecializationConstants,
117+
const SpecializationConstant* pSpecializationConstants) noexcept(false);
118+
113119

114120
/// Copies ray tracing shader group names and also initializes the mapping from the group name to its index.
115121
void CopyRTShaderGroupNames(std::unordered_map<HashMapStringKey, Uint32>& NameToGroupIndex,
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* Copyright 2019-2026 Diligent Graphics LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* In no event and under no legal theory, whether in tort (including negligence),
17+
* contract, or otherwise, unless required by applicable law (such as deliberate
18+
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
19+
* liable for any damages, including any direct, indirect, special, incidental,
20+
* or consequential damages of any character arising as a result of this License or
21+
* out of the use or inability to use the software (including but not limited to damages
22+
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
23+
* all other commercial damages or losses), even if such Contributor has been advised
24+
* of the possibility of such damages.
25+
*/
26+
27+
#include "../../../../Graphics/GraphicsEngine/include/PipelineStateBase.hpp"
28+
29+
#include "TestingEnvironment.hpp"
30+
31+
#include "gtest/gtest.h"
32+
33+
using namespace Diligent;
34+
using namespace Diligent::Testing;
35+
36+
namespace
37+
{
38+
39+
class ValidateSpecializationConstantsTest : public ::testing::Test
40+
{
41+
protected:
42+
static DeviceFeatures GetEnabledFeatures()
43+
{
44+
DeviceFeatures Features;
45+
Features.SpecializationConstants = DEVICE_FEATURE_STATE_ENABLED;
46+
return Features;
47+
}
48+
49+
static DeviceFeatures GetDisabledFeatures()
50+
{
51+
DeviceFeatures Features;
52+
Features.SpecializationConstants = DEVICE_FEATURE_STATE_DISABLED;
53+
return Features;
54+
}
55+
56+
PipelineStateDesc PSODesc{};
57+
DeviceFeatures EnabledFeatures = GetEnabledFeatures();
58+
DeviceFeatures DisabledFeatures = GetDisabledFeatures();
59+
};
60+
61+
62+
TEST_F(ValidateSpecializationConstantsTest, NullPointerWithNonZeroCount)
63+
{
64+
PSODesc.Name = "TestPSO";
65+
66+
{
67+
TestingEnvironment::ErrorScope ExpectedErrors{"pSpecializationConstants is null"};
68+
EXPECT_THROW(
69+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, nullptr),
70+
std::runtime_error);
71+
}
72+
73+
// Zero count with null pointer should not throw
74+
EXPECT_NO_THROW(
75+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 0, nullptr));
76+
}
77+
78+
79+
TEST_F(ValidateSpecializationConstantsTest, FeatureDisabled)
80+
{
81+
PSODesc.Name = "TestPSO";
82+
83+
const float Data = 1.0f;
84+
85+
SpecializationConstant SpecConsts[] = {
86+
{"Constant0", SHADER_TYPE_VERTEX, sizeof(Data), &Data},
87+
};
88+
89+
TestingEnvironment::ErrorScope ExpectedErrors{"SpecializationConstants device feature is not enabled"};
90+
EXPECT_THROW(
91+
ValidateSpecializationConstants(PSODesc, DisabledFeatures, 1, SpecConsts),
92+
std::runtime_error);
93+
}
94+
95+
96+
TEST_F(ValidateSpecializationConstantsTest, NullName)
97+
{
98+
PSODesc.Name = "TestPSO";
99+
100+
const float Data = 1.0f;
101+
102+
SpecializationConstant SpecConsts[] = {
103+
{nullptr, SHADER_TYPE_VERTEX, sizeof(Data), &Data},
104+
};
105+
106+
TestingEnvironment::ErrorScope ExpectedErrors{"Name must not be null"};
107+
EXPECT_THROW(
108+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, SpecConsts),
109+
std::runtime_error);
110+
}
111+
112+
113+
TEST_F(ValidateSpecializationConstantsTest, EmptyName)
114+
{
115+
PSODesc.Name = "TestPSO";
116+
117+
const float Data = 1.0f;
118+
119+
SpecializationConstant SpecConsts[] = {
120+
{"", SHADER_TYPE_VERTEX, sizeof(Data), &Data},
121+
};
122+
123+
TestingEnvironment::ErrorScope ExpectedErrors{"Name must not be empty"};
124+
EXPECT_THROW(
125+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, SpecConsts),
126+
std::runtime_error);
127+
}
128+
129+
130+
TEST_F(ValidateSpecializationConstantsTest, UnknownShaderStages)
131+
{
132+
PSODesc.Name = "TestPSO";
133+
134+
const float Data = 1.0f;
135+
136+
SpecializationConstant SpecConsts[] = {
137+
{"Constant0", SHADER_TYPE_UNKNOWN, sizeof(Data), &Data},
138+
};
139+
140+
TestingEnvironment::ErrorScope ExpectedErrors{"ShaderStages must not be SHADER_TYPE_UNKNOWN"};
141+
EXPECT_THROW(
142+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, SpecConsts),
143+
std::runtime_error);
144+
}
145+
146+
147+
TEST_F(ValidateSpecializationConstantsTest, ZeroSize)
148+
{
149+
PSODesc.Name = "TestPSO";
150+
151+
const float Data = 1.0f;
152+
153+
SpecializationConstant SpecConsts[] = {
154+
{"Constant0", SHADER_TYPE_VERTEX, 0, &Data},
155+
};
156+
157+
TestingEnvironment::ErrorScope ExpectedErrors{"Size must not be zero"};
158+
EXPECT_THROW(
159+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, SpecConsts),
160+
std::runtime_error);
161+
}
162+
163+
164+
TEST_F(ValidateSpecializationConstantsTest, NullData)
165+
{
166+
PSODesc.Name = "TestPSO";
167+
168+
SpecializationConstant SpecConsts[] = {
169+
{"Constant0", SHADER_TYPE_VERTEX, sizeof(float), nullptr},
170+
};
171+
172+
TestingEnvironment::ErrorScope ExpectedErrors{"pData must not be null"};
173+
EXPECT_THROW(
174+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 1, SpecConsts),
175+
std::runtime_error);
176+
}
177+
178+
179+
TEST_F(ValidateSpecializationConstantsTest, DuplicateNameOverlappingStages)
180+
{
181+
PSODesc.Name = "TestPSO";
182+
183+
const float Data = 1.0f;
184+
185+
SpecializationConstant SpecConsts[] = {
186+
{"Constant0", SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, sizeof(Data), &Data},
187+
{"Constant0", SHADER_TYPE_VERTEX | SHADER_TYPE_GEOMETRY, sizeof(Data), &Data},
188+
};
189+
190+
TestingEnvironment::ErrorScope ExpectedErrors{"is defined in overlapping shader stages"};
191+
EXPECT_THROW(
192+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 2, SpecConsts),
193+
std::runtime_error);
194+
}
195+
196+
197+
TEST_F(ValidateSpecializationConstantsTest, DuplicateNameNonOverlappingStages)
198+
{
199+
PSODesc.Name = "TestPSO";
200+
201+
const float Data = 1.0f;
202+
203+
SpecializationConstant SpecConsts[] = {
204+
{"Constant0", SHADER_TYPE_VERTEX, sizeof(Data), &Data},
205+
{"Constant0", SHADER_TYPE_PIXEL, sizeof(Data), &Data},
206+
};
207+
208+
EXPECT_NO_THROW(
209+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 2, SpecConsts));
210+
}
211+
212+
213+
TEST_F(ValidateSpecializationConstantsTest, ValidConstants)
214+
{
215+
PSODesc.Name = "TestPSO";
216+
217+
const float FloatData = 1.0f;
218+
const int IntData = 42;
219+
220+
SpecializationConstant SpecConsts[] = {
221+
{"FloatConst", SHADER_TYPE_VERTEX, sizeof(FloatData), &FloatData},
222+
{"IntConst", SHADER_TYPE_PIXEL, sizeof(IntData), &IntData},
223+
};
224+
225+
EXPECT_NO_THROW(
226+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 2, SpecConsts));
227+
}
228+
229+
230+
TEST_F(ValidateSpecializationConstantsTest, ErrorAtSecondElement)
231+
{
232+
PSODesc.Name = "TestPSO";
233+
234+
const float Data = 1.0f;
235+
236+
// First element is valid, second has null name
237+
SpecializationConstant SpecConsts[] = {
238+
{"Constant0", SHADER_TYPE_VERTEX, sizeof(Data), &Data},
239+
{nullptr, SHADER_TYPE_PIXEL, sizeof(Data), &Data},
240+
};
241+
242+
TestingEnvironment::ErrorScope ExpectedErrors{"pSpecializationConstants[1].Name must not be null"};
243+
EXPECT_THROW(
244+
ValidateSpecializationConstants(PSODesc, EnabledFeatures, 2, SpecConsts),
245+
std::runtime_error);
246+
}
247+
248+
} // namespace

0 commit comments

Comments
 (0)