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