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 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 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_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 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 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 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 199 SpvAsmGraphicsSpirvVersionsInstance::SpvAsmGraphicsSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters) 200 : TestInstance (ctx) 201 , m_testParameters (testParameters) 202 { 203 } 204 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 226 SpvAsmComputeSpirvVersionsInstance::SpvAsmComputeSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters) 227 : ComputeShaderSpec(getComputeShaderSpec(testParameters)) 228 , SpvAsmComputeShaderInstance(ctx, *this, COMPUTE_TEST_USES_NONE) 229 , m_testParameters(testParameters) 230 { 231 if (m_testParameters.operation != OPERATION_COMPUTE) 232 TCU_THROW(InternalError, "Invalid operation specified"); 233 } 234 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 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 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 269 void SpvAsmSpirvVersionsCase::initPrograms (SourceCollections& programCollection) const 270 { 271 const SpirVAsmBuildOptions spirVAsmBuildOptions (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 338 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 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