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<deUint32>(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 {
73 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
74 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
75 };
76 static const ShaderElement tessPipelineStages[] =
77 {
78 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
79 ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
80 ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
81 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
82 };
83 static const ShaderElement geomPipelineStages[] =
84 {
85 ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
86 ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY_BIT),
87 ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
88 };
89 map<string, string> opSimpleTest;
90
91 opSimpleTest["testfun"] =
92 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
93 "%param1 = OpFunctionParameter %v4f32\n"
94 "%label_testfun = OpLabel\n"
95 "%a = OpVectorExtractDynamic %f32 %param1 %c_i32_0\n"
96 "%b = OpFAdd %f32 %a %a\n"
97 "%c = OpFSub %f32 %b %a\n"
98 "%ret = OpVectorInsertDynamic %v4f32 %param1 %c %c_i32_0\n"
99 "OpReturnValue %ret\n"
100 "OpFunctionEnd\n";
101
102 switch (testParameters.operation)
103 {
104 case OPERATION_GRAPHICS_VERTEX: return createInstanceContext(vertFragPipelineStages, opSimpleTest);
105 case OPERATION_GRAPHICS_TESSELATION_EVALUATION: return createInstanceContext(tessPipelineStages, opSimpleTest);
106 case OPERATION_GRAPHICS_TESSELATION_CONTROL: return createInstanceContext(tessPipelineStages, opSimpleTest);
107 case OPERATION_GRAPHICS_GEOMETRY: return createInstanceContext(geomPipelineStages, opSimpleTest);
108 case OPERATION_GRAPHICS_FRAGMENT: return createInstanceContext(vertFragPipelineStages, opSimpleTest);
109 default: TCU_THROW(InternalError, "Invalid operation specified");
110 }
111 }
112
getComputeSourceCode(std::string & computeSourceCode)113 static void getComputeSourceCode (std::string& computeSourceCode)
114 {
115 computeSourceCode =
116 string(getComputeAsmShaderPreamble()) +
117
118 "OpSource GLSL 430\n"
119 "OpName %main \"main\"\n"
120 "OpName %id \"gl_GlobalInvocationID\"\n"
121
122 "OpDecorate %id BuiltIn GlobalInvocationId\n" +
123
124 string(getComputeAsmInputOutputBufferTraits()) +
125 string(getComputeAsmCommonTypes()) +
126 string(getComputeAsmInputOutputBuffer()) +
127
128 "%id = OpVariable %uvec3ptr Input\n"
129 "%zero = OpConstant %i32 0\n"
130
131 "%main = OpFunction %void None %voidf\n"
132 "%label = OpLabel\n"
133 "%idval = OpLoad %uvec3 %id\n"
134 "%x = OpCompositeExtract %u32 %idval 0\n"
135
136 " OpNop\n" // Inside a function body
137
138 "%inloc = OpAccessChain %f32ptr %indata %zero %x\n"
139 "%inval = OpLoad %f32 %inloc\n"
140 "%neg = OpFNegate %f32 %inval\n"
141 "%outloc = OpAccessChain %f32ptr %outdata %zero %x\n"
142 " OpStore %outloc %neg\n"
143 " OpReturn\n"
144 " OpFunctionEnd\n";
145 }
146
getComputeShaderSpec(const TestParameters & testParameters)147 static ComputeShaderSpec getComputeShaderSpec (const TestParameters& testParameters)
148 {
149 ComputeShaderSpec spec;
150 const deUint32 seed = (static_cast<deUint32>(testParameters.operation)<<16) ^ static_cast<deUint32>(testParameters.spirvVersion);
151 de::Random rnd (seed);
152 const int numElements = 100;
153 vector<float> positiveFloats (numElements, 0);
154 vector<float> negativeFloats (numElements, 0);
155
156 for (size_t ndx = 0; ndx < numElements; ++ndx)
157 {
158 positiveFloats[ndx] = rnd.getFloat(1.0f, 100.0f);
159 negativeFloats[ndx] = -positiveFloats[ndx];
160 }
161
162 // Shader source code can be retrieved to complete definition of ComputeShaderSpec, though it is not required at this stage
163 // getComputeSourceCode (spec.assembly);
164
165 spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
166 spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
167 spec.numWorkGroups = tcu::IVec3(numElements, 1, 1);
168
169 return spec;
170 }
171
isSpirVersionsAsRequested(const BinaryCollection & binaryCollection,SpirvVersion requestedSpirvVersion)172 static bool isSpirVersionsAsRequested (const BinaryCollection& binaryCollection, SpirvVersion requestedSpirvVersion)
173 {
174 bool result = true;
175
176 DE_ASSERT(!binaryCollection.empty());
177
178 for (vk::BinaryCollection::Iterator binaryIt = binaryCollection.begin(); binaryIt != binaryCollection.end(); ++binaryIt)
179 {
180 SpirvVersion binarySpirvVersion = extractSpirvVersion (binaryIt.getProgram());
181
182 if (binarySpirvVersion != requestedSpirvVersion)
183 result = false;
184 }
185
186 return result;
187 }
188
189 class SpvAsmGraphicsSpirvVersionsInstance : public TestInstance
190 {
191 public:
192 SpvAsmGraphicsSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters);
193 tcu::TestStatus iterate (void);
194
195 private:
196 TestParameters m_testParameters;
197 };
198
SpvAsmGraphicsSpirvVersionsInstance(Context & ctx,const TestParameters & testParameters)199 SpvAsmGraphicsSpirvVersionsInstance::SpvAsmGraphicsSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters)
200 : TestInstance (ctx)
201 , m_testParameters (testParameters)
202 {
203 }
204
iterate(void)205 tcu::TestStatus SpvAsmGraphicsSpirvVersionsInstance::iterate (void)
206 {
207 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
208
209 if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
210 return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
211
212 return runAndVerifyDefaultPipeline(m_context, instanceContext);
213 }
214
215
216 class SpvAsmComputeSpirvVersionsInstance : public ComputeShaderSpec, public SpvAsmComputeShaderInstance
217 {
218 public:
219 SpvAsmComputeSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters);
220 tcu::TestStatus iterate (void);
221
222 private:
223 TestParameters m_testParameters;
224 };
225
SpvAsmComputeSpirvVersionsInstance(Context & ctx,const TestParameters & testParameters)226 SpvAsmComputeSpirvVersionsInstance::SpvAsmComputeSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters)
227 : ComputeShaderSpec(getComputeShaderSpec(testParameters))
228 , SpvAsmComputeShaderInstance(ctx, *this)
229 , m_testParameters(testParameters)
230 {
231 if (m_testParameters.operation != OPERATION_COMPUTE)
232 TCU_THROW(InternalError, "Invalid operation specified");
233 }
234
iterate(void)235 tcu::TestStatus SpvAsmComputeSpirvVersionsInstance::iterate (void)
236 {
237 if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
238 return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
239
240 return SpvAsmComputeShaderInstance::iterate();
241 }
242
243
244 class SpvAsmSpirvVersionsCase : public TestCase
245 {
246 public:
247 SpvAsmSpirvVersionsCase (tcu::TestContext& testCtx, const char* name, const char* description, const TestParameters& testParameters);
248 void initPrograms (vk::SourceCollections& programCollection) const;
249 TestInstance* createInstance (Context& context) const;
250
251 private:
252 const TestParameters m_testParameters;
253 };
254
SpvAsmSpirvVersionsCase(tcu::TestContext & testCtx,const char * name,const char * description,const TestParameters & testParameters)255 SpvAsmSpirvVersionsCase::SpvAsmSpirvVersionsCase (tcu::TestContext& testCtx, const char* name, const char* description, const TestParameters& testParameters)
256 : TestCase (testCtx, name, description)
257 , m_testParameters (testParameters)
258 {
259 }
260
validateVulkanVersion(const deUint32 usedVulkanVersion,const SpirvVersion testedSpirvVersion)261 void validateVulkanVersion (const deUint32 usedVulkanVersion, const SpirvVersion testedSpirvVersion)
262 {
263 const SpirvVersion usedSpirvVersionForAsm = getMaxSpirvVersionForAsm(usedVulkanVersion);
264
265 if (testedSpirvVersion > usedSpirvVersionForAsm)
266 TCU_THROW(NotSupportedError, "Specified SPIR-V version is not supported by the device/instance");
267 }
268
initPrograms(SourceCollections & programCollection) const269 void SpvAsmSpirvVersionsCase::initPrograms (SourceCollections& programCollection) const
270 {
271 const SpirVAsmBuildOptions spirVAsmBuildOptions (programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
272
273 validateVulkanVersion(programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
274
275 switch (m_testParameters.operation)
276 {
277 case OPERATION_COMPUTE:
278 {
279 std::string comp;
280
281 getComputeSourceCode(comp);
282
283 programCollection.spirvAsmSources.add("compute", &spirVAsmBuildOptions) << comp;
284
285 break;
286 }
287
288 case OPERATION_GRAPHICS_VERTEX:
289 {
290 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
291
292 addShaderCodeCustomVertex(programCollection, instanceContext, &spirVAsmBuildOptions);
293
294 break;
295 }
296
297 case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
298 {
299 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
300
301 addShaderCodeCustomTessEval(programCollection, instanceContext, &spirVAsmBuildOptions);
302
303 break;
304 }
305
306 case OPERATION_GRAPHICS_TESSELATION_CONTROL:
307 {
308 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
309
310 addShaderCodeCustomTessControl(programCollection, instanceContext, &spirVAsmBuildOptions);
311
312 break;
313 }
314
315 case OPERATION_GRAPHICS_GEOMETRY:
316 {
317 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
318
319 addShaderCodeCustomGeometry(programCollection, instanceContext, &spirVAsmBuildOptions);
320
321 break;
322 }
323
324 case OPERATION_GRAPHICS_FRAGMENT:
325 {
326 InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
327
328 addShaderCodeCustomFragment(programCollection, instanceContext, &spirVAsmBuildOptions);
329
330 break;
331 }
332
333 default:
334 TCU_THROW(InternalError, "Invalid operation specified");
335 }
336 }
337
createInstance(Context & context) const338 TestInstance* SpvAsmSpirvVersionsCase::createInstance (Context& context) const
339 {
340 validateVulkanVersion(context.getUsedApiVersion(), m_testParameters.spirvVersion);
341
342 switch (m_testParameters.operation)
343 {
344 case OPERATION_COMPUTE:
345 return new SpvAsmComputeSpirvVersionsInstance(context, m_testParameters);
346
347 case OPERATION_GRAPHICS_VERTEX:
348 case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
349 case OPERATION_GRAPHICS_TESSELATION_CONTROL:
350 case OPERATION_GRAPHICS_GEOMETRY:
351 case OPERATION_GRAPHICS_FRAGMENT:
352 return new SpvAsmGraphicsSpirvVersionsInstance(context, m_testParameters);
353
354 default:
355 TCU_THROW(InternalError, "Invalid operation specified");
356 }
357 }
358
createSpivVersionCheckTests(tcu::TestContext & testCtx,const bool compute)359 tcu::TestCaseGroup* createSpivVersionCheckTests (tcu::TestContext& testCtx, const bool compute)
360 {
361 const char* operationNames[OPERATION_LAST] =
362 {
363 "compute",
364 "vertex",
365 "tesselation_evaluation",
366 "tesselation_control",
367 "geometry",
368 "fragment",
369 };
370
371 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "spirv_version", "Test SPIR-V version is supported"));
372
373 for (SpirvVersion spirvVersion = SPIRV_VERSION_1_0; spirvVersion < SPIRV_VERSION_LAST; ++spirvVersion)
374 {
375 std::string spirvVersionName = getSpirvVersionName(spirvVersion);
376
377 std::replace(spirvVersionName.begin(), spirvVersionName.end(), '.', '_');
378
379 for (Operation operation = OPERATION_COMPUTE; operation < OPERATION_LAST; ++operation)
380 {
381 if ((compute && operation == OPERATION_COMPUTE) || (!compute && operation != OPERATION_COMPUTE))
382 {
383 const std::string testName = spirvVersionName + "_" + operationNames[static_cast<deUint32>(operation)];
384 const TestParameters testParameters =
385 {
386 operation,
387 spirvVersion
388 };
389
390 group->addChild(new SpvAsmSpirvVersionsCase(testCtx, testName.c_str(), "", testParameters));
391 }
392 }
393 }
394
395 return group.release();
396 }
397
398 } // SpirVAssembly
399 } // vkt
400