1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2017 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief SPIR-V Versions check cases
22 *//*--------------------------------------------------------------------*/
23
24 #include "vkApiVersion.hpp"
25
26 #include "vktSpvAsmSpirvVersionTests.hpp"
27 #include "vktTestCase.hpp"
28 #include "vktSpvAsmComputeShaderCase.hpp"
29 #include "vktSpvAsmGraphicsShaderTestUtil.hpp"
30
31 namespace vkt
32 {
33 namespace SpirVAssembly
34 {
35
36 using namespace vk;
37 using std::map;
38 using std::string;
39 using std::vector;
40 using tcu::RGBA;
41
42 enum Operation
43 {
44 OPERATION_COMPUTE = 0,
45 OPERATION_GRAPHICS_VERTEX,
46 OPERATION_GRAPHICS_TESSELATION_EVALUATION,
47 OPERATION_GRAPHICS_TESSELATION_CONTROL,
48 OPERATION_GRAPHICS_GEOMETRY,
49 OPERATION_GRAPHICS_FRAGMENT,
50 OPERATION_LAST
51 };
52
operator ++(Operation & operation)53 Operation &operator++(Operation &operation)
54 {
55 if (operation == OPERATION_LAST)
56 operation = OPERATION_COMPUTE;
57 else
58 operation = static_cast<Operation>(static_cast<uint32_t>(operation) + 1);
59
60 return operation;
61 }
62
63 struct TestParameters
64 {
65 Operation operation;
66 SpirvVersion spirvVersion;
67 };
68
initGraphicsInstanceContext(const TestParameters & testParameters)69 static InstanceContext initGraphicsInstanceContext(const TestParameters &testParameters)
70 {
71 static const ShaderElement vertFragPipelineStages[] = {
72 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
73 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
74 };
75 static const ShaderElement tessPipelineStages[] = {
76 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
77 ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
78 ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
79 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
80 };
81 static const ShaderElement geomPipelineStages[] = {
82 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
83 ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY_BIT),
84 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
85 };
86 map<string, string> opSimpleTest;
87
88 opSimpleTest["testfun"] = "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
89 "%param1 = OpFunctionParameter %v4f32\n"
90 "%label_testfun = OpLabel\n"
91 "%a = OpVectorExtractDynamic %f32 %param1 %c_i32_0\n"
92 "%b = OpFAdd %f32 %a %a\n"
93 "%c = OpFSub %f32 %b %a\n"
94 "%ret = OpVectorInsertDynamic %v4f32 %param1 %c %c_i32_0\n"
95 "OpReturnValue %ret\n"
96 "OpFunctionEnd\n";
97
98 if (testParameters.spirvVersion > SPIRV_VERSION_1_3)
99 opSimpleTest["GL_entrypoint"] = "%BP_vertexIdInCurrentPatch";
100
101 switch (testParameters.operation)
102 {
103 case OPERATION_GRAPHICS_VERTEX:
104 return createInstanceContext(vertFragPipelineStages, opSimpleTest);
105 case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
106 return createInstanceContext(tessPipelineStages, opSimpleTest);
107 case OPERATION_GRAPHICS_TESSELATION_CONTROL:
108 return createInstanceContext(tessPipelineStages, opSimpleTest);
109 case OPERATION_GRAPHICS_GEOMETRY:
110 return createInstanceContext(geomPipelineStages, opSimpleTest);
111 case OPERATION_GRAPHICS_FRAGMENT:
112 return createInstanceContext(vertFragPipelineStages, opSimpleTest);
113 default:
114 TCU_THROW(InternalError, "Invalid operation specified");
115 }
116 }
117
getComputeSourceCode(std::string & computeSourceCode,SpirvVersion spirvVersion)118 static void getComputeSourceCode(std::string &computeSourceCode, SpirvVersion spirvVersion)
119 {
120 computeSourceCode = "";
121 if (spirvVersion > SPIRV_VERSION_1_3)
122 computeSourceCode += string(getComputeAsmShaderPreamble("", "", "", "", "%indata %outdata"));
123 else
124 computeSourceCode += string(getComputeAsmShaderPreamble());
125
126 computeSourceCode +=
127 "OpSource GLSL 430\n"
128 "OpName %main \"main\"\n"
129 "OpName %id \"gl_GlobalInvocationID\"\n"
130
131 "OpDecorate %id BuiltIn GlobalInvocationId\n" +
132
133 string(getComputeAsmInputOutputBufferTraits((spirvVersion > SPIRV_VERSION_1_3) ? "Block" : "BufferBlock")) +
134 string(getComputeAsmCommonTypes((spirvVersion > SPIRV_VERSION_1_3) ? "StorageBuffer" : "Uniform")) +
135 string(getComputeAsmInputOutputBuffer((spirvVersion > SPIRV_VERSION_1_3) ? "StorageBuffer" : "Uniform")) +
136
137 "%id = OpVariable %uvec3ptr Input\n"
138 "%zero = OpConstant %i32 0\n"
139
140 "%main = OpFunction %void None %voidf\n"
141 "%label = OpLabel\n"
142 "%idval = OpLoad %uvec3 %id\n"
143 "%x = OpCompositeExtract %u32 %idval 0\n"
144
145 " OpNop\n" // Inside a function body
146
147 "%inloc = OpAccessChain %f32ptr %indata %zero %x\n"
148 "%inval = OpLoad %f32 %inloc\n"
149 "%neg = OpFNegate %f32 %inval\n"
150 "%outloc = OpAccessChain %f32ptr %outdata %zero %x\n"
151 " OpStore %outloc %neg\n"
152 " OpReturn\n"
153 " OpFunctionEnd\n";
154 }
155
getComputeShaderSpec(const TestParameters & testParameters)156 static ComputeShaderSpec getComputeShaderSpec(const TestParameters &testParameters)
157 {
158 ComputeShaderSpec spec;
159 const uint32_t seed =
160 (static_cast<uint32_t>(testParameters.operation) << 16) ^ static_cast<uint32_t>(testParameters.spirvVersion);
161 de::Random rnd(seed);
162 const int numElements = 100;
163 vector<float> positiveFloats(numElements, 0);
164 vector<float> negativeFloats(numElements, 0);
165
166 for (size_t ndx = 0; ndx < numElements; ++ndx)
167 {
168 positiveFloats[ndx] = rnd.getFloat(1.0f, 100.0f);
169 negativeFloats[ndx] = -positiveFloats[ndx];
170 }
171
172 // Shader source code can be retrieved to complete definition of ComputeShaderSpec, though it is not required at this stage
173 // getComputeSourceCode (spec.assembly);
174
175 spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
176 spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
177 spec.numWorkGroups = tcu::IVec3(numElements, 1, 1);
178
179 return spec;
180 }
181
isSpirVersionsAsRequested(const BinaryCollection & binaryCollection,SpirvVersion requestedSpirvVersion)182 static bool isSpirVersionsAsRequested(const BinaryCollection &binaryCollection, SpirvVersion requestedSpirvVersion)
183 {
184 bool result = true;
185
186 DE_ASSERT(!binaryCollection.empty());
187
188 for (vk::BinaryCollection::Iterator binaryIt = binaryCollection.begin(); binaryIt != binaryCollection.end();
189 ++binaryIt)
190 {
191 SpirvVersion binarySpirvVersion = extractSpirvVersion(binaryIt.getProgram());
192
193 if (binarySpirvVersion != requestedSpirvVersion)
194 result = false;
195 }
196
197 return result;
198 }
199
200 class SpvAsmGraphicsSpirvVersionsInstance : public TestInstance
201 {
202 public:
203 SpvAsmGraphicsSpirvVersionsInstance(Context &ctx, const TestParameters &testParameters);
204 tcu::TestStatus iterate(void);
205
206 private:
207 TestParameters m_testParameters;
208 };
209
SpvAsmGraphicsSpirvVersionsInstance(Context & ctx,const TestParameters & testParameters)210 SpvAsmGraphicsSpirvVersionsInstance::SpvAsmGraphicsSpirvVersionsInstance(Context &ctx,
211 const TestParameters &testParameters)
212 : TestInstance(ctx)
213 , m_testParameters(testParameters)
214 {
215 }
216
iterate(void)217 tcu::TestStatus SpvAsmGraphicsSpirvVersionsInstance::iterate(void)
218 {
219 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
220
221 if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
222 return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
223
224 return runAndVerifyDefaultPipeline(m_context, instanceContext);
225 }
226
227 class SpvAsmComputeSpirvVersionsInstance : public ComputeShaderSpec, public SpvAsmComputeShaderInstance
228 {
229 public:
230 SpvAsmComputeSpirvVersionsInstance(Context &ctx, const TestParameters &testParameters);
231 tcu::TestStatus iterate(void);
232
233 private:
234 TestParameters m_testParameters;
235 };
236
SpvAsmComputeSpirvVersionsInstance(Context & ctx,const TestParameters & testParameters)237 SpvAsmComputeSpirvVersionsInstance::SpvAsmComputeSpirvVersionsInstance(Context &ctx,
238 const TestParameters &testParameters)
239 : ComputeShaderSpec(getComputeShaderSpec(testParameters))
240 , SpvAsmComputeShaderInstance(ctx, *this)
241 , m_testParameters(testParameters)
242 {
243 if (m_testParameters.operation != OPERATION_COMPUTE)
244 TCU_THROW(InternalError, "Invalid operation specified");
245 }
246
iterate(void)247 tcu::TestStatus SpvAsmComputeSpirvVersionsInstance::iterate(void)
248 {
249 if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
250 return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
251
252 return SpvAsmComputeShaderInstance::iterate();
253 }
254
255 class SpvAsmSpirvVersionsCase : public TestCase
256 {
257 public:
258 SpvAsmSpirvVersionsCase(tcu::TestContext &testCtx, const char *name, const TestParameters &testParameters);
259 void initPrograms(vk::SourceCollections &programCollection) const;
260 TestInstance *createInstance(Context &context) const;
261
262 private:
263 const TestParameters m_testParameters;
264 };
265
SpvAsmSpirvVersionsCase(tcu::TestContext & testCtx,const char * name,const TestParameters & testParameters)266 SpvAsmSpirvVersionsCase::SpvAsmSpirvVersionsCase(tcu::TestContext &testCtx, const char *name,
267 const TestParameters &testParameters)
268 : TestCase(testCtx, name)
269 , m_testParameters(testParameters)
270 {
271 }
272
validateVulkanVersion(const uint32_t usedVulkanVersion,const SpirvVersion testedSpirvVersion)273 void validateVulkanVersion(const uint32_t usedVulkanVersion, const SpirvVersion testedSpirvVersion)
274 {
275 const SpirvVersion usedSpirvVersionForAsm = getMaxSpirvVersionForAsm(usedVulkanVersion);
276
277 if (testedSpirvVersion > usedSpirvVersionForAsm)
278 TCU_THROW(NotSupportedError, "Specified SPIR-V version is not supported by the device/instance");
279 }
280
initPrograms(SourceCollections & programCollection) const281 void SpvAsmSpirvVersionsCase::initPrograms(SourceCollections &programCollection) const
282 {
283 const SpirVAsmBuildOptions spirVAsmBuildOptions(programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
284
285 validateVulkanVersion(programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
286
287 switch (m_testParameters.operation)
288 {
289 case OPERATION_COMPUTE:
290 {
291 std::string comp;
292
293 getComputeSourceCode(comp, m_testParameters.spirvVersion);
294
295 programCollection.spirvAsmSources.add("compute", &spirVAsmBuildOptions) << comp;
296
297 break;
298 }
299
300 case OPERATION_GRAPHICS_VERTEX:
301 {
302 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
303
304 addShaderCodeCustomVertex(programCollection, instanceContext, &spirVAsmBuildOptions);
305
306 break;
307 }
308
309 case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
310 {
311 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
312
313 addShaderCodeCustomTessEval(programCollection, instanceContext, &spirVAsmBuildOptions);
314
315 break;
316 }
317
318 case OPERATION_GRAPHICS_TESSELATION_CONTROL:
319 {
320 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
321
322 addShaderCodeCustomTessControl(programCollection, instanceContext, &spirVAsmBuildOptions);
323
324 break;
325 }
326
327 case OPERATION_GRAPHICS_GEOMETRY:
328 {
329 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
330
331 addShaderCodeCustomGeometry(programCollection, instanceContext, &spirVAsmBuildOptions);
332
333 break;
334 }
335
336 case OPERATION_GRAPHICS_FRAGMENT:
337 {
338 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
339
340 addShaderCodeCustomFragment(programCollection, instanceContext, &spirVAsmBuildOptions);
341
342 break;
343 }
344
345 default:
346 TCU_THROW(InternalError, "Invalid operation specified");
347 }
348 }
349
createInstance(Context & context) const350 TestInstance *SpvAsmSpirvVersionsCase::createInstance(Context &context) const
351 {
352 validateVulkanVersion(context.getUsedApiVersion(), m_testParameters.spirvVersion);
353
354 switch (m_testParameters.operation)
355 {
356 case OPERATION_COMPUTE:
357 return new SpvAsmComputeSpirvVersionsInstance(context, m_testParameters);
358
359 case OPERATION_GRAPHICS_VERTEX:
360 case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
361 case OPERATION_GRAPHICS_TESSELATION_CONTROL:
362 case OPERATION_GRAPHICS_GEOMETRY:
363 case OPERATION_GRAPHICS_FRAGMENT:
364 return new SpvAsmGraphicsSpirvVersionsInstance(context, m_testParameters);
365
366 default:
367 TCU_THROW(InternalError, "Invalid operation specified");
368 }
369 }
370
createSpivVersionCheckTests(tcu::TestContext & testCtx,const bool compute)371 tcu::TestCaseGroup *createSpivVersionCheckTests(tcu::TestContext &testCtx, const bool compute)
372 {
373 const char *operationNames[OPERATION_LAST] = {
374 "compute", "vertex", "tesselation_evaluation", "tesselation_control", "geometry", "fragment",
375 };
376
377 // Test SPIR-V version is supported
378 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "spirv_version"));
379
380 for (SpirvVersion spirvVersion = SPIRV_VERSION_1_0; spirvVersion < SPIRV_VERSION_LAST; ++spirvVersion)
381 {
382 std::string spirvVersionName = getSpirvVersionName(spirvVersion);
383
384 std::replace(spirvVersionName.begin(), spirvVersionName.end(), '.', '_');
385
386 for (Operation operation = OPERATION_COMPUTE; operation < OPERATION_LAST; ++operation)
387 {
388 if ((compute && operation == OPERATION_COMPUTE) || (!compute && operation != OPERATION_COMPUTE))
389 {
390 const std::string testName = spirvVersionName + "_" + operationNames[static_cast<uint32_t>(operation)];
391 const TestParameters testParameters = {operation, spirvVersion};
392
393 group->addChild(new SpvAsmSpirvVersionsCase(testCtx, testName.c_str(), testParameters));
394 }
395 }
396 }
397
398 return group.release();
399 }
400
401 } // namespace SpirVAssembly
402 } // namespace vkt
403