1 #ifndef _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP 2 #define _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP 3 /*------------------------------------------------------------------------- 4 * Vulkan Conformance Tests 5 * ------------------------ 6 * 7 * Copyright (c) 2017 Google Inc. 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 *//*! 22 * \file 23 * \brief Graphics pipeline and helper functions for SPIR-V assembly tests 24 *//*--------------------------------------------------------------------*/ 25 26 #include "tcuCommandLine.hpp" 27 #include "tcuRGBA.hpp" 28 29 #include "vkPrograms.hpp" 30 #include "vktSpvAsmComputeShaderTestUtil.hpp" 31 #include "vktSpvAsmUtils.hpp" 32 #include "vktTestCaseUtil.hpp" 33 34 #include "deRandom.hpp" 35 #include "deSharedPtr.hpp" 36 37 #include <map> 38 #include <sstream> 39 #include <string> 40 #include <utility> 41 42 namespace vkt 43 { 44 namespace SpirVAssembly 45 { 46 47 typedef vk::Unique<VkBuffer> BufferHandleUp; 48 typedef de::SharedPtr<BufferHandleUp> BufferHandleSp; 49 typedef vk::Unique<vk::VkShaderModule> ModuleHandleUp; 50 typedef de::SharedPtr<ModuleHandleUp> ModuleHandleSp; 51 typedef std::pair<std::string, vk::VkShaderStageFlagBits> EntryToStage; 52 typedef std::map<std::string, std::vector<EntryToStage> > ModuleMap; 53 typedef std::map<vk::VkShaderStageFlagBits, std::vector<deInt32> > StageToSpecConstantMap; 54 typedef std::pair<vk::VkDescriptorType, BufferSp> Resource; 55 56 enum NumberType 57 { 58 NUMBERTYPE_INT32, 59 NUMBERTYPE_UINT32, 60 NUMBERTYPE_FLOAT32, 61 NUMBERTYPE_END32, // Marks the end of 32-bit scalar types 62 NUMBERTYPE_INT16, 63 NUMBERTYPE_UINT16, 64 NUMBERTYPE_FLOAT16, 65 }; 66 67 typedef enum RoundingModeFlags_e 68 { 69 ROUNDINGMODE_RTE = 0x1, // Round to nearest even 70 ROUNDINGMODE_RTZ = 0x2, // Round to zero 71 } RoundingModeFlags; 72 73 typedef bool (*GraphicsVerifyIOFunc) (const std::vector<Resource>& inputs, 74 const std::vector<AllocationSp>& outputAllocations, 75 const std::vector<Resource>& expectedOutputs, 76 tcu::TestLog& log); 77 78 typedef bool (*GraphicsVerifyBinaryFunc) (const ProgramBinary& binary); 79 80 // Resources used by graphics-pipeline-based tests. 81 struct GraphicsResources 82 { 83 // Resources used as inputs. 84 std::vector<Resource> inputs; 85 // Resources used as outputs. The data supplied will be used as 86 // the expected outputs for the corresponding bindings by default. 87 // If other behaviors are needed, please provide a custom verifyIO. 88 std::vector<Resource> outputs; 89 // If null, a default verification will be performed by comparing the 90 // memory pointed to by outputAllocations and the contents of 91 // expectedOutputs. Otherwise the function pointed to by verifyIO will 92 // be called. If true is returned, then the test case is assumed to 93 // have passed, if false is returned, then the test case is assumed 94 // to have failed. 95 GraphicsVerifyIOFunc verifyIO; 96 GraphicsVerifyBinaryFunc verifyBinary; 97 SpirvVersion spirvVersion; 98 99 GraphicsResources() 100 : verifyIO (DE_NULL) 101 , verifyBinary (DE_NULL) 102 , spirvVersion (SPIRV_VERSION_1_0) 103 {} 104 }; 105 106 // Interface data type. 107 struct IFDataType 108 { 109 IFDataType (deUint32 numE, NumberType elementT) 110 : numElements (numE) 111 , elementType (elementT) 112 { 113 DE_ASSERT(numE > 0 && numE < 5); 114 DE_ASSERT(elementT != NUMBERTYPE_END32); 115 } 116 117 IFDataType (const IFDataType& that) 118 : numElements (that.numElements) 119 , elementType (that.elementType) 120 {} 121 122 deUint32 getElementNumBytes (void) const; 123 deUint32 getNumBytes (void) const { return numElements * getElementNumBytes(); } 124 125 vk::VkFormat getVkFormat (void) const; 126 127 tcu::TextureFormat getTextureFormat (void) const; 128 129 std::string str (void) const; 130 131 bool elementIs32bit (void) const { return elementType < NUMBERTYPE_END32; } 132 bool isVector (void) const { return numElements > 1; } 133 134 deUint32 numElements; 135 NumberType elementType; 136 }; 137 138 typedef std::pair<IFDataType, BufferSp> Interface; 139 140 // Interface variables used by graphics-pipeline-based tests. 141 class GraphicsInterfaces 142 { 143 public: 144 GraphicsInterfaces () 145 : rndMode (static_cast<RoundingModeFlags>(0)) 146 {} 147 148 GraphicsInterfaces (const GraphicsInterfaces& that) 149 : inputs (that.inputs) 150 , outputs (that.outputs) 151 , rndMode (that.rndMode) 152 {} 153 154 void setInputOutput (const Interface& input, const Interface& output) 155 { 156 inputs.clear(); 157 outputs.clear(); 158 inputs.push_back(input); 159 outputs.push_back(output); 160 } 161 162 const IFDataType& getInputType (void) const 163 { 164 DE_ASSERT(inputs.size() == 1); 165 return inputs.front().first; 166 } 167 168 const IFDataType& getOutputType (void) const 169 { 170 DE_ASSERT(outputs.size() == 1); 171 return outputs.front().first; 172 } 173 174 const BufferSp& getInputBuffer (void) const 175 { 176 DE_ASSERT(inputs.size() == 1); 177 return inputs.front().second; 178 } 179 180 const BufferSp& getOutputBuffer (void) const 181 { 182 DE_ASSERT(outputs.size() == 1); 183 return outputs.front().second; 184 } 185 186 bool empty (void) const 187 { 188 return inputs.size() == 0; 189 } 190 191 void setRoundingMode (RoundingModeFlags flag) 192 { 193 rndMode = flag; 194 } 195 RoundingModeFlags getRoundingMode (void) const 196 { 197 return rndMode; 198 } 199 private: 200 // vector<Interface> acts as a null-able Interface here. Canonically we should use 201 // std::unique_ptr, but sadly we cannot leverage C++11 in dEQP. dEQP has its own 202 // de::UniquePtr, but still cumbersome to use in InstanceContext and do copies 203 // at various places. 204 // Public methods should make sure that there are less than two elements in both 205 // members and both members have the same number of elements. 206 std::vector<Interface> inputs; 207 std::vector<Interface> outputs; 208 RoundingModeFlags rndMode; 209 210 }; 211 212 struct PushConstants 213 { 214 public: 215 PushConstants (void) 216 {} 217 218 PushConstants (const PushConstants& that) 219 : pcs (that.pcs) 220 {} 221 222 void setPushConstant (const BufferSp& pc) 223 { 224 pcs.clear(); 225 pcs.push_back(pc); 226 } 227 228 bool empty (void) const 229 { 230 return pcs.empty(); 231 } 232 233 const BufferSp& getBuffer(void) const 234 { 235 DE_ASSERT(pcs.size() == 1); 236 return pcs[0]; 237 } 238 239 private: 240 // Right now we only support one field in the push constant block. 241 std::vector<BufferSp> pcs; 242 }; 243 244 // Returns the corresponding buffer usage flag bit for the given descriptor type. 245 VkBufferUsageFlagBits getMatchingBufferUsageFlagBit(VkDescriptorType dType); 246 247 // Context for a specific test instantiation. For example, an instantiation 248 // may test colors yellow/magenta/cyan/mauve in a tesselation shader 249 // with an entry point named 'main_to_the_main' 250 struct InstanceContext 251 { 252 // Map of modules to what entry_points we care to use from those modules. 253 ModuleMap moduleMap; 254 tcu::RGBA inputColors[4]; 255 tcu::RGBA outputColors[4]; 256 // Concrete SPIR-V code to test via boilerplate specialization. 257 std::map<std::string, std::string> testCodeFragments; 258 StageToSpecConstantMap specConstants; 259 bool hasTessellation; 260 vk::VkShaderStageFlagBits requiredStages; 261 std::vector<std::string> requiredDeviceExtensions; 262 std::vector<std::string> requiredDeviceFeatures; 263 VulkanFeatures requestedFeatures; 264 PushConstants pushConstants; 265 // Specifies the (one or more) stages that use a customized shader code. 266 VkShaderStageFlags customizedStages; 267 // Possible resources used by the graphics pipeline. 268 // If it is not empty, a single descriptor set (number 0) will be allocated 269 // to point to all resources specified. Binding numbers are allocated in 270 // accord with the resources' order in the vector; outputs are allocated 271 // after inputs. 272 GraphicsResources resources; 273 // Possible interface variables use by the graphics pipeline. 274 // If it is not empty, input/output variables will be set up for shader stages 275 // in the test. Both the input and output variable will take location #2 in the 276 // pipeline for all stages, except that the output variable in the fragment 277 // stage will take location #1. 278 GraphicsInterfaces interfaces; 279 qpTestResult failResult; 280 std::string failMessageTemplate; //!< ${reason} in the template will be replaced with a detailed failure message 281 282 InstanceContext (const tcu::RGBA (&inputs)[4], 283 const tcu::RGBA (&outputs)[4], 284 const std::map<std::string, std::string>& testCodeFragments_, 285 const StageToSpecConstantMap& specConstants_, 286 const PushConstants& pushConsants_, 287 const GraphicsResources& resources_, 288 const GraphicsInterfaces& interfaces_, 289 const std::vector<std::string>& extensions_, 290 const std::vector<std::string>& features_, 291 VulkanFeatures vulkanFeatures_, 292 VkShaderStageFlags customizedStages_); 293 294 InstanceContext (const InstanceContext& other); 295 296 std::string getSpecializedFailMessage (const std::string& failureReason); 297 }; 298 299 // A description of a shader to be used for a single stage of the graphics pipeline. 300 struct ShaderElement 301 { 302 // The module that contains this shader entrypoint. 303 std::string moduleName; 304 305 // The name of the entrypoint. 306 std::string entryName; 307 308 // Which shader stage this entry point represents. 309 vk::VkShaderStageFlagBits stage; 310 311 ShaderElement (const std::string& moduleName_, const std::string& entryPoint_, vk::VkShaderStageFlagBits shaderStage_); 312 }; 313 314 template <typename T> 315 const std::string numberToString (T number) 316 { 317 std::stringstream ss; 318 ss << number; 319 return ss.str(); 320 } 321 322 // Performs a bitwise copy of source to the destination type Dest. 323 template <typename Dest, typename Src> 324 Dest bitwiseCast(Src source) 325 { 326 Dest dest; 327 DE_STATIC_ASSERT(sizeof(source) == sizeof(dest)); 328 deMemcpy(&dest, &source, sizeof(dest)); 329 return dest; 330 } 331 332 template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue); 333 template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); } 334 template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); } 335 336 337 void getDefaultColors (tcu::RGBA (&colors)[4]); 338 339 void getHalfColorsFullAlpha (tcu::RGBA (&colors)[4]); 340 341 void getInvertedDefaultColors (tcu::RGBA (&colors)[4]); 342 343 // Creates fragments that specialize into a simple pass-through shader (of any kind). 344 std::map<std::string, std::string> passthruFragments(void); 345 346 void createCombinedModule(vk::SourceCollections& dst, InstanceContext); 347 348 // This has two shaders of each stage. The first 349 // is a passthrough, the second inverts the color. 350 void createMultipleEntries(vk::SourceCollections& dst, InstanceContext); 351 352 // Turns a statically sized array of ShaderElements into an instance-context 353 // by setting up the mapping of modules to their contained shaders and stages. 354 // The inputs and expected outputs are given by inputColors and outputColors 355 template<size_t N> 356 InstanceContext createInstanceContext (const ShaderElement (&elements)[N], 357 const tcu::RGBA (&inputColors)[4], 358 const tcu::RGBA (&outputColors)[4], 359 const std::map<std::string, std::string>& testCodeFragments, 360 const StageToSpecConstantMap& specConstants, 361 const PushConstants& pushConstants, 362 const GraphicsResources& resources, 363 const GraphicsInterfaces& interfaces, 364 const std::vector<std::string>& extensions, 365 const std::vector<std::string>& features, 366 VulkanFeatures vulkanFeatures, 367 VkShaderStageFlags customizedStages, 368 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 369 const std::string& failMessageTemplate = std::string()) 370 { 371 InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources, interfaces, extensions, features, vulkanFeatures, customizedStages); 372 for (size_t i = 0; i < N; ++i) 373 { 374 ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage)); 375 ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage); 376 } 377 ctx.failResult = failResult; 378 if (!failMessageTemplate.empty()) 379 ctx.failMessageTemplate = failMessageTemplate; 380 return ctx; 381 } 382 383 // The same as createInstanceContext above, without extensions, spec constants, and resources. 384 template<size_t N> 385 inline InstanceContext createInstanceContext (const ShaderElement (&elements)[N], 386 tcu::RGBA (&inputColors)[4], 387 const tcu::RGBA (&outputColors)[4], 388 const std::map<std::string, std::string>& testCodeFragments) 389 { 390 return createInstanceContext(elements, inputColors, outputColors, testCodeFragments, 391 StageToSpecConstantMap(), PushConstants(), GraphicsResources(), 392 GraphicsInterfaces(), std::vector<std::string>(), std::vector<std::string>(), 393 VulkanFeatures(), vk::VK_SHADER_STAGE_ALL); 394 } 395 396 // The same as createInstanceContext above, but with default colors. 397 template<size_t N> 398 InstanceContext createInstanceContext (const ShaderElement (&elements)[N], 399 const std::map<std::string, std::string>& testCodeFragments) 400 { 401 tcu::RGBA defaultColors[4]; 402 getDefaultColors(defaultColors); 403 return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments); 404 } 405 406 407 void addShaderCodeCustomVertex(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions); 408 void addShaderCodeCustomTessControl(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions); 409 void addShaderCodeCustomTessEval(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions); 410 void addShaderCodeCustomGeometry(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions); 411 void addShaderCodeCustomFragment(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions); 412 413 void createTestsForAllStages (const std::string& name, 414 const tcu::RGBA (&inputColors)[4], 415 const tcu::RGBA (&outputColors)[4], 416 const std::map<std::string, std::string>& testCodeFragments, 417 const std::vector<deInt32>& specConstants, 418 const PushConstants& pushConstants, 419 const GraphicsResources& resources, 420 const GraphicsInterfaces& interfaces, 421 const std::vector<std::string>& extensions, 422 const std::vector<std::string>& features, 423 VulkanFeatures vulkanFeatures, 424 tcu::TestCaseGroup* tests, 425 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 426 const std::string& failMessageTemplate = std::string()); 427 428 inline void createTestsForAllStages (const std::string& name, 429 const tcu::RGBA (&inputColors)[4], 430 const tcu::RGBA (&outputColors)[4], 431 const std::map<std::string, std::string>& testCodeFragments, 432 tcu::TestCaseGroup* tests, 433 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 434 const std::string& failMessageTemplate = std::string()) 435 { 436 std::vector<deInt32> noSpecConstants; 437 PushConstants noPushConstants; 438 GraphicsResources noResources; 439 GraphicsInterfaces noInterfaces; 440 std::vector<std::string> noExtensions; 441 std::vector<std::string> noFeatures; 442 443 createTestsForAllStages( 444 name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants, 445 noResources, noInterfaces, noExtensions, noFeatures, VulkanFeatures(), 446 tests, failResult, failMessageTemplate); 447 } 448 449 inline void createTestsForAllStages (const std::string& name, 450 const tcu::RGBA (&inputColors)[4], 451 const tcu::RGBA (&outputColors)[4], 452 const std::map<std::string, std::string>& testCodeFragments, 453 const std::vector<deInt32>& specConstants, 454 tcu::TestCaseGroup* tests, 455 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 456 const std::string& failMessageTemplate = std::string()) 457 { 458 PushConstants noPushConstants; 459 GraphicsResources noResources; 460 GraphicsInterfaces noInterfaces; 461 std::vector<std::string> noExtensions; 462 std::vector<std::string> noFeatures; 463 464 createTestsForAllStages( 465 name, inputColors, outputColors, testCodeFragments, specConstants, noPushConstants, 466 noResources, noInterfaces, noExtensions, noFeatures, VulkanFeatures(), 467 tests, failResult, failMessageTemplate); 468 } 469 470 inline void createTestsForAllStages (const std::string& name, 471 const tcu::RGBA (&inputColors)[4], 472 const tcu::RGBA (&outputColors)[4], 473 const std::map<std::string, std::string>& testCodeFragments, 474 const GraphicsResources& resources, 475 const std::vector<std::string>& extensions, 476 tcu::TestCaseGroup* tests, 477 VulkanFeatures vulkanFeatures = VulkanFeatures(), 478 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 479 const std::string& failMessageTemplate = std::string()) 480 { 481 std::vector<deInt32> noSpecConstants; 482 PushConstants noPushConstants; 483 GraphicsInterfaces noInterfaces; 484 std::vector<std::string> noFeatures; 485 486 createTestsForAllStages( 487 name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants, 488 resources, noInterfaces, extensions, noFeatures, vulkanFeatures, 489 tests, failResult, failMessageTemplate); 490 } 491 492 inline void createTestsForAllStages (const std::string& name, 493 const tcu::RGBA (&inputColors)[4], 494 const tcu::RGBA (&outputColors)[4], 495 const std::map<std::string, std::string>& testCodeFragments, 496 const GraphicsInterfaces interfaces, 497 const std::vector<std::string>& extensions, 498 tcu::TestCaseGroup* tests, 499 VulkanFeatures vulkanFeatures = VulkanFeatures(), 500 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 501 const std::string& failMessageTemplate = std::string()) 502 { 503 GraphicsResources noResources; 504 std::vector<deInt32> noSpecConstants; 505 std::vector<std::string> noFeatures; 506 PushConstants noPushConstants; 507 508 createTestsForAllStages( 509 name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants, 510 noResources, interfaces, extensions, noFeatures, vulkanFeatures, 511 tests, failResult, failMessageTemplate); 512 } 513 514 inline void createTestsForAllStages (const std::string& name, 515 const tcu::RGBA (&inputColors)[4], 516 const tcu::RGBA (&outputColors)[4], 517 const std::map<std::string, std::string>& testCodeFragments, 518 const PushConstants& pushConstants, 519 const GraphicsResources& resources, 520 const std::vector<std::string>& extensions, 521 tcu::TestCaseGroup* tests, 522 VulkanFeatures vulkanFeatures = VulkanFeatures(), 523 const qpTestResult failResult = QP_TEST_RESULT_FAIL, 524 const std::string& failMessageTemplate = std::string()) 525 { 526 std::vector<deInt32> noSpecConstants; 527 GraphicsInterfaces noInterfaces; 528 std::vector<std::string> noFeatures; 529 530 createTestsForAllStages( 531 name, inputColors, outputColors, testCodeFragments, noSpecConstants, pushConstants, 532 resources, noInterfaces, extensions, noFeatures, vulkanFeatures, 533 tests, failResult, failMessageTemplate); 534 } 535 536 // Sets up and runs a Vulkan pipeline, then spot-checks the resulting image. 537 // Feeds the pipeline a set of colored triangles, which then must occur in the 538 // rendered image. The surface is cleared before executing the pipeline, so 539 // whatever the shaders draw can be directly spot-checked. 540 tcu::TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance); 541 542 // Adds a new test to group using custom fragments for the tessellation-control 543 // stage and passthrough fragments for all other stages. Uses default colors 544 // for input and expected output. 545 void addTessCtrlTest(tcu::TestCaseGroup* group, const char* name, const std::map<std::string, std::string>& fragments); 546 547 // Given the original 32-bit float value, computes the corresponding 16-bit 548 // float value under the given rounding mode flags and compares with the 549 // returned 16-bit float value. Returns true if they are considered as equal. 550 // 551 // The following equivalence criteria are respected: 552 // * Positive and negative zeros are considered equivalent. 553 // * Denormalized floats are allowed to be flushed to zeros, including 554 // * Inputted 32bit denormalized float 555 // * Generated 16bit denormalized float 556 // * Different bit patterns of NaNs are allowed. 557 // * For the rest, require exactly the same bit pattern. 558 bool compare16BitFloat (float original, deUint16 returned, RoundingModeFlags flags, tcu::TestLog& log); 559 560 // Compare the returned 32-bit float against its expected value. 561 // 562 // The following equivalence criteria are respected: 563 // * Denormalized floats are allowed to be flushed to zeros, including 564 // * The expected value itself is a denormalized float 565 // * The expected value is a denormalized float if converted to 16bit 566 // * Different bit patterns of NaNs/Infs are allowed. 567 // * For the rest, use C++ float equivalence check. 568 bool compare32BitFloat (float expected, float returned, tcu::TestLog& log); 569 570 } // SpirVAssembly 571 } // vkt 572 573 #endif // _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP 574