1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2018 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 Robust buffer access tests for storage buffers and 22 * storage texel buffers with variable pointers. 23 * 24 * \note These tests are checking if accessing a memory through a variable 25 * pointer that points outside of accessible buffer memory is robust. 26 * To do this the tests are creating proper SPIRV code that creates 27 * variable pointers. Those pointers are either pointing into a 28 * memory allocated for a buffer but "not accesible" - meaning 29 * DescriptorBufferInfo has smaller size than a memory we access in 30 * shader or entirely outside of allocated memory (i.e. buffer is 31 * 256 bytes big but we are trying to access under offset of 1k from 32 * buffer start). There is a set of valid behaviours defined when 33 * robust buffer access extension is enabled described in chapter 32 34 * section 1 of Vulkan spec. 35 * 36 *//*--------------------------------------------------------------------*/ 37 38 #include "vktRobustBufferAccessWithVariablePointersTests.hpp" 39 #include "vktRobustnessUtil.hpp" 40 #include "vktTestCaseUtil.hpp" 41 #include "vkBuilderUtil.hpp" 42 #include "vkImageUtil.hpp" 43 #include "vkPrograms.hpp" 44 #include "vkQueryUtil.hpp" 45 #include "vkRef.hpp" 46 #include "vkRefUtil.hpp" 47 #include "vkTypeUtil.hpp" 48 #include "tcuTestLog.hpp" 49 #include "vkDefs.hpp" 50 #include "deRandom.hpp" 51 52 #include <limits> 53 #include <sstream> 54 55 namespace vkt 56 { 57 namespace robustness 58 { 59 60 using namespace vk; 61 62 // keep local things local 63 namespace 64 { 65 66 // A function for getting information on variable pointer features supported through physical device 67 vk::VkPhysicalDeviceVariablePointerFeatures querySupportedVariablePointersFeatures (const deUint32 apiVersion, 68 const InstanceInterface& vki, 69 VkPhysicalDevice device, 70 const std::vector<std::string>& instanceExtensions) 71 { 72 VkPhysicalDeviceVariablePointerFeatures extensionFeatures = 73 { 74 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR, // sType 75 DE_NULL, // pNext 76 false, // variablePointersStorageBuffer 77 false, // variablePointers 78 }; 79 80 VkPhysicalDeviceFeatures2 features; 81 deMemset(&features, 0, sizeof(features)); 82 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 83 features.pNext = &extensionFeatures; 84 85 // Call the getter only if supported. Otherwise above "zero" defaults are used 86 if (isInstanceExtensionSupported(apiVersion, instanceExtensions, "VK_KHR_get_physical_device_properties2")) 87 { 88 vki.getPhysicalDeviceFeatures2(device, &features); 89 } 90 91 return extensionFeatures; 92 } 93 94 // A supplementary structures that can hold information about buffer size 95 struct AccessRangesData 96 { 97 VkDeviceSize allocSize; 98 VkDeviceSize accessRange; 99 VkDeviceSize maxAccessRange; 100 }; 101 102 // Pointer to function that can be used to fill a buffer with some data - it is passed as an parameter to buffer creation utility function 103 typedef void(*FillBufferProcPtr)(void*, vk::VkDeviceSize, const void* const); 104 105 // An utility function for creating a buffer 106 // This function not only allocates memory for the buffer but also fills buffer up with a data 107 void createTestBuffer (const vk::DeviceInterface& deviceInterface, 108 const VkDevice& device, 109 VkDeviceSize accessRange, 110 VkBufferUsageFlags usage, 111 SimpleAllocator& allocator, 112 Move<VkBuffer>& buffer, 113 de::MovePtr<Allocation>& bufferAlloc, 114 AccessRangesData& data, 115 FillBufferProcPtr fillBufferProc, 116 const void* const blob) 117 { 118 const VkBufferCreateInfo bufferParams = 119 { 120 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; 121 DE_NULL, // const void* pNext; 122 0u, // VkBufferCreateFlags flags; 123 accessRange, // VkDeviceSize size; 124 usage, // VkBufferUsageFlags usage; 125 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 126 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount; 127 DE_NULL // const deUint32* pQueueFamilyIndices; 128 }; 129 130 buffer = createBuffer(deviceInterface, device, &bufferParams); 131 132 VkMemoryRequirements bufferMemoryReqs = getBufferMemoryRequirements(deviceInterface, device, *buffer); 133 bufferAlloc = allocator.allocate(bufferMemoryReqs, MemoryRequirement::HostVisible); 134 135 data.allocSize = bufferMemoryReqs.size; 136 data.accessRange = accessRange; 137 data.maxAccessRange = deMinu64(data.allocSize, deMinu64(bufferParams.size, accessRange)); 138 139 VK_CHECK(deviceInterface.bindBufferMemory(device, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset())); 140 fillBufferProc(bufferAlloc->getHostPtr(), bufferMemoryReqs.size, blob); 141 flushMappedMemoryRange(deviceInterface, device, bufferAlloc->getMemory(), bufferAlloc->getOffset(), VK_WHOLE_SIZE); 142 } 143 144 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with "randomly" generated test data matching desired format. 145 void populateBufferWithValues (void* buffer, 146 VkDeviceSize size, 147 const void* const blob) 148 { 149 populateBufferWithTestValues(buffer, size, *static_cast<const vk::VkFormat*>(blob)); 150 } 151 152 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with 0xBABABABABABA... pattern. Used to fill up output buffers. 153 // Since this pattern cannot show up in generated test data it should not show up in the valid output. 154 void populateBufferWithDummy (void* buffer, 155 VkDeviceSize size, 156 const void* const blob) 157 { 158 DE_UNREF(blob); 159 deMemset(buffer, 0xBA, static_cast<size_t>(size)); 160 } 161 162 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with a copy of memory contents pointed to by blob. 163 void populateBufferWithCopy (void* buffer, 164 VkDeviceSize size, 165 const void* const blob) 166 { 167 deMemcpy(buffer, blob, static_cast<size_t>(size)); 168 } 169 170 // A composite types used in test 171 // Those composites can be made of unsigned ints, signed ints or floats (except for matrices that work with floats only). 172 enum ShaderType 173 { 174 SHADER_TYPE_MATRIX_COPY = 0, 175 SHADER_TYPE_VECTOR_COPY, 176 SHADER_TYPE_SCALAR_COPY, 177 178 SHADER_TYPE_COUNT 179 }; 180 181 // We are testing reads or writes 182 // In case of testing reads - writes are always 183 enum BufferAccessType 184 { 185 BUFFER_ACCESS_TYPE_READ_FROM_STORAGE = 0, 186 BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE, 187 }; 188 189 // Test case for checking robust buffer access with variable pointers 190 class RobustAccessWithPointersTest : public vkt::TestCase 191 { 192 public: 193 static const deUint32 s_testArraySize; 194 static const deUint32 s_numberOfBytesAccessed; 195 196 RobustAccessWithPointersTest (tcu::TestContext& testContext, 197 const std::string& name, 198 const std::string& description, 199 VkShaderStageFlags shaderStage, 200 ShaderType shaderType, 201 VkFormat bufferFormat); 202 203 virtual ~RobustAccessWithPointersTest (void) 204 { 205 } 206 207 protected: 208 const VkShaderStageFlags m_shaderStage; 209 const ShaderType m_shaderType; 210 const VkFormat m_bufferFormat; 211 }; 212 213 const deUint32 RobustAccessWithPointersTest::s_testArraySize = 1024u; 214 const deUint32 RobustAccessWithPointersTest::s_numberOfBytesAccessed = static_cast<deUint32>(16ull * sizeof(float)); 215 216 RobustAccessWithPointersTest::RobustAccessWithPointersTest(tcu::TestContext& testContext, 217 const std::string& name, 218 const std::string& description, 219 VkShaderStageFlags shaderStage, 220 ShaderType shaderType, 221 VkFormat bufferFormat) 222 : vkt::TestCase(testContext, name, description) 223 , m_shaderStage(shaderStage) 224 , m_shaderType(shaderType) 225 , m_bufferFormat(bufferFormat) 226 { 227 DE_ASSERT(m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT || m_shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT || m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT); 228 } 229 230 // A subclass for testing reading with variable pointers 231 class RobustReadTest : public RobustAccessWithPointersTest 232 { 233 public: 234 RobustReadTest (tcu::TestContext& testContext, 235 const std::string& name, 236 const std::string& description, 237 VkShaderStageFlags shaderStage, 238 ShaderType shaderType, 239 VkFormat bufferFormat, 240 VkDeviceSize readAccessRange, 241 bool accessOutOfBackingMemory); 242 243 virtual ~RobustReadTest (void) 244 {} 245 virtual TestInstance* createInstance (Context& context) const; 246 private: 247 virtual void initPrograms (SourceCollections& programCollection) const; 248 const VkDeviceSize m_readAccessRange; 249 const bool m_accessOutOfBackingMemory; 250 }; 251 252 // A subclass for testing writing with variable pointers 253 class RobustWriteTest : public RobustAccessWithPointersTest 254 { 255 public: 256 RobustWriteTest (tcu::TestContext& testContext, 257 const std::string& name, 258 const std::string& description, 259 VkShaderStageFlags shaderStage, 260 ShaderType shaderType, 261 VkFormat bufferFormat, 262 VkDeviceSize writeAccessRange, 263 bool accessOutOfBackingMemory); 264 265 virtual ~RobustWriteTest (void) {} 266 virtual TestInstance* createInstance (Context& context) const; 267 private: 268 virtual void initPrograms (SourceCollections& programCollection) const; 269 const VkDeviceSize m_writeAccessRange; 270 const bool m_accessOutOfBackingMemory; 271 }; 272 273 // In case I detect that some prerequisites are not fullfilled I am creating this lightweight dummy test instance instead of AccessInstance. Should be bit faster that way. 274 class NotSupportedInstance : public vkt::TestInstance 275 { 276 public: 277 NotSupportedInstance (Context& context, 278 const std::string& message) 279 : TestInstance(context) 280 , m_notSupportedMessage(message) 281 {} 282 283 virtual ~NotSupportedInstance (void) 284 { 285 } 286 287 virtual tcu::TestStatus iterate (void) 288 { 289 TCU_THROW(NotSupportedError, m_notSupportedMessage.c_str()); 290 } 291 292 private: 293 std::string m_notSupportedMessage; 294 }; 295 296 // A superclass for instances testing reading and writing 297 // holds all necessary object members 298 class AccessInstance : public vkt::TestInstance 299 { 300 public: 301 AccessInstance (Context& context, 302 Move<VkDevice> device, 303 ShaderType shaderType, 304 VkShaderStageFlags shaderStage, 305 VkFormat bufferFormat, 306 BufferAccessType bufferAccessType, 307 VkDeviceSize inBufferAccessRange, 308 VkDeviceSize outBufferAccessRange, 309 bool accessOutOfBackingMemory); 310 311 virtual ~AccessInstance (void) {} 312 313 virtual tcu::TestStatus iterate (void); 314 315 virtual bool verifyResult (void); 316 317 private: 318 bool isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, 319 const void* valuePtr, 320 VkDeviceSize valueSize); 321 bool isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, 322 VkDeviceSize valueSize); 323 324 protected: 325 Move<VkDevice> m_device; 326 de::MovePtr<TestEnvironment>m_testEnvironment; 327 328 const ShaderType m_shaderType; 329 const VkShaderStageFlags m_shaderStage; 330 331 const VkFormat m_bufferFormat; 332 const BufferAccessType m_bufferAccessType; 333 334 AccessRangesData m_inBufferAccess; 335 Move<VkBuffer> m_inBuffer; 336 de::MovePtr<Allocation> m_inBufferAlloc; 337 338 AccessRangesData m_outBufferAccess; 339 Move<VkBuffer> m_outBuffer; 340 de::MovePtr<Allocation> m_outBufferAlloc; 341 342 Move<VkBuffer> m_indicesBuffer; 343 de::MovePtr<Allocation> m_indicesBufferAlloc; 344 345 Move<VkDescriptorPool> m_descriptorPool; 346 Move<VkDescriptorSetLayout> m_descriptorSetLayout; 347 Move<VkDescriptorSet> m_descriptorSet; 348 349 Move<VkFence> m_fence; 350 VkQueue m_queue; 351 352 // Used when m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT 353 Move<VkBuffer> m_vertexBuffer; 354 de::MovePtr<Allocation> m_vertexBufferAlloc; 355 356 const bool m_accessOutOfBackingMemory; 357 }; 358 359 // A subclass for read tests 360 class ReadInstance: public AccessInstance 361 { 362 public: 363 ReadInstance (Context& context, 364 Move<VkDevice> device, 365 ShaderType shaderType, 366 VkShaderStageFlags shaderStage, 367 VkFormat bufferFormat, 368 VkDeviceSize inBufferAccessRange, 369 bool accessOutOfBackingMemory); 370 371 virtual ~ReadInstance (void) {} 372 }; 373 374 // A subclass for write tests 375 class WriteInstance: public AccessInstance 376 { 377 public: 378 WriteInstance (Context& context, 379 Move<VkDevice> device, 380 ShaderType shaderType, 381 VkShaderStageFlags shaderStage, 382 VkFormat bufferFormat, 383 VkDeviceSize writeBufferAccessRange, 384 bool accessOutOfBackingMemory); 385 386 virtual ~WriteInstance (void) {} 387 }; 388 389 // Automatically incremented counter. 390 // Each read of value bumps counter up. 391 class Autocounter 392 { 393 public: 394 Autocounter() 395 :value(0u) 396 {} 397 deUint32 incrementAndGetValue() 398 { 399 return ++value; 400 } 401 private: 402 deUint32 value; 403 }; 404 405 // A class representing SPIRV variable. 406 // This class internally has an unique identificator. 407 // When such variable is used in shader composition routine it is mapped on a in-SPIRV-code variable name. 408 class Variable 409 { 410 friend bool operator < (const Variable& a, const Variable& b); 411 public: 412 Variable(Autocounter& autoincrement) 413 : value(autoincrement.incrementAndGetValue()) 414 {} 415 private: 416 deUint32 value; 417 }; 418 419 bool operator < (const Variable& a, const Variable& b) 420 { 421 return a.value < b.value; 422 } 423 424 // A class representing SPIRV operation. 425 // Since those are not copyable they don't need internal id. Memory address is used instead. 426 class Operation 427 { 428 friend bool operator==(const Operation& a, const Operation& b); 429 public: 430 Operation(const char* text) 431 : value(text) 432 { 433 } 434 const std::string& getValue() const 435 { 436 return value; 437 } 438 439 private: 440 Operation(const Operation& other); 441 const std::string value; 442 }; 443 444 bool operator == (const Operation& a, const Operation& b) 445 { 446 return &a == &b; // a fast & simple address comparison - making copies was disabled 447 } 448 449 // A namespace containing all SPIRV operations used in those tests. 450 namespace op { 451 #define OP(name) const Operation name("Op"#name) 452 OP(Capability); 453 OP(Extension); 454 OP(ExtInstImport); 455 OP(EntryPoint); 456 OP(MemoryModel); 457 OP(ExecutionMode); 458 459 OP(Decorate); 460 OP(MemberDecorate); 461 OP(Name); 462 OP(MemberName); 463 464 OP(TypeVoid); 465 OP(TypeBool); 466 OP(TypeInt); 467 OP(TypeFloat); 468 OP(TypeVector); 469 OP(TypeMatrix); 470 OP(TypeArray); 471 OP(TypeStruct); 472 OP(TypeFunction); 473 OP(TypePointer); 474 OP(TypeImage); 475 OP(TypeSampledImage); 476 477 OP(Constant); 478 OP(ConstantComposite); 479 OP(Variable); 480 481 OP(Function); 482 OP(FunctionEnd); 483 OP(Label); 484 OP(Return); 485 486 OP(LogicalEqual); 487 OP(IEqual); 488 OP(Select); 489 490 OP(AccessChain); 491 OP(Load); 492 OP(Store); 493 #undef OP 494 } 495 496 // A class that allows to easily compose SPIRV code. 497 // This class automatically keeps correct order of most of operations 498 // i.e. capabilities to the top, 499 class ShaderStream 500 { 501 public: 502 ShaderStream () 503 {} 504 // composes shader string out of shader substreams. 505 std::string str () const 506 { 507 std::stringstream stream; 508 stream << capabilities.str() 509 << "; ----------------- PREAMBLE -----------------\n" 510 << preamble.str() 511 << "; ----------------- DEBUG --------------------\n" 512 << names.str() 513 << "; ----------------- DECORATIONS --------------\n" 514 << decorations.str() 515 << "; ----------------- TYPES --------------------\n" 516 << basictypes.str() 517 << "; ----------------- CONSTANTS ----------------\n" 518 << constants.str() 519 << "; ----------------- ADVANCED TYPES -----------\n" 520 << compositetypes.str() 521 << ((compositeconstants.str().length() > 0) ? "; ----------------- CONSTANTS ----------------\n" : "") 522 << compositeconstants.str() 523 << "; ----------------- VARIABLES & FUNCTIONS ----\n" 524 << shaderstream.str(); 525 return stream.str(); 526 } 527 // Functions below are used to push Operations, Variables and other strings, numbers and characters to the shader. 528 // Each function uses selectStream and map subroutines. 529 // selectStream is used to choose a proper substream of shader. 530 // E.g. if an operation is OpConstant it should be put into constants definitions stream - so selectStream will return that stream. 531 // map on the other hand is used to replace Variables and Operations to their in-SPIRV-code representations. 532 // for types like ints or floats map simply calls << operator to produce its string representation 533 // for Operations a proper operation string is returned 534 // for Variables there is a special mapping between in-C++ variable and in-SPIRV-code variable name. 535 // following sequence of functions could be squashed to just two using variadic templates once we move to C++11 or higher 536 // each method returns *this to allow chaining calls to these methods. 537 template <typename T> 538 ShaderStream& operator () (const T& a) 539 { 540 selectStream(a, 0) << map(a) << '\n'; 541 return *this; 542 } 543 template <typename T1, typename T2> 544 ShaderStream& operator () (const T1& a, const T2& b) 545 { 546 selectStream(a, 0) << map(a) << '\t' << map(b) << '\n'; 547 return *this; 548 } 549 template <typename T1, typename T2, typename T3> 550 ShaderStream& operator () (const T1& a, const T2& b, const T3& c) 551 { 552 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\n'; 553 return *this; 554 } 555 template <typename T1, typename T2, typename T3, typename T4> 556 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d) 557 { 558 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\n'; 559 return *this; 560 } 561 template <typename T1, typename T2, typename T3, typename T4, typename T5> 562 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e) 563 { 564 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\n'; 565 return *this; 566 } 567 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> 568 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f) 569 { 570 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\n'; 571 return *this; 572 } 573 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> 574 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f, const T7& g) 575 { 576 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\t' << map(g) << '\n'; 577 return *this; 578 } 579 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> 580 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f, const T7& g, const T8& h) 581 { 582 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\t' << map(g) << '\t' << map(h) << '\n'; 583 return *this; 584 } 585 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> 586 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f, const T7& g, const T8& h, const T9& i) 587 { 588 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\t' << map(g) << '\t' << map(h) << '\t' << map(i) << '\n'; 589 return *this; 590 } 591 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10> 592 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f, const T7& g, const T8& h, const T9& i, const T10& k) 593 { 594 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\t' << map(g) << '\t' << map(h) << '\t' << map(i) << '\t' << map(k) << '\n'; 595 return *this; 596 } 597 598 // returns true if two variables has the same in-SPIRV-code names 599 bool areSame (const Variable a, const Variable b) 600 { 601 VariableIt varA = vars.find(a); 602 VariableIt varB = vars.find(b); 603 return varA != vars.end() && varB != vars.end() && varA->second == varB->second; 604 } 605 606 // makes variable 'a' in-SPIRV-code name to be the same as variable 'b' in-SPIRV-code name 607 void makeSame (const Variable a, const Variable b) 608 { 609 VariableIt varB = vars.find(b); 610 if (varB != vars.end()) 611 { 612 std::pair<VariableIt, bool> inserted = vars.insert(std::make_pair(a, varB->second)); 613 if (!inserted.second) 614 inserted.first->second = varB->second; 615 } 616 } 617 private: 618 // generic version of map (tries to push whatever came to stringstream to get its string representation) 619 template <typename T> 620 std::string map (const T& a) 621 { 622 std::stringstream temp; 623 temp << a; 624 return temp.str(); 625 } 626 627 // looks for mapping of c++ Variable object onto in-SPIRV-code name. 628 // if there was not yet such mapping generated a new mapping is created based on incremented local counter. 629 std::string map (const Variable& a) 630 { 631 VariableIt var = vars.find(a); 632 if (var != vars.end()) 633 return var->second; 634 std::stringstream temp; 635 temp << '%'; 636 temp.width(4); 637 temp.fill('0'); 638 temp << std::hex << varCounter.incrementAndGetValue(); 639 vars.insert(std::make_pair(a, temp.str())); 640 return temp.str(); 641 } 642 643 // a simple specification for Operation 644 std::string map (const Operation& a) 645 { 646 return a.getValue(); 647 } 648 649 // a specification for char* - faster than going through stringstream << operator 650 std::string map (const char*& a) 651 { 652 return std::string(a); 653 } 654 655 // a specification for char - faster than going through stringstream << operator 656 std::string map (const char& a) 657 { 658 return std::string(1, a); 659 } 660 661 // a generic version of selectStream - used when neither 1st nor 3rd SPIRV line token is Operation. 662 // In general should never happen. 663 // All SPIRV lines are constructed in a one of two forms: 664 // Variable = Operation operands... 665 // or 666 // Operation operands... 667 // So operation is either 1st or 3rd token. 668 template <typename T0, typename T1> 669 std::stringstream& selectStream (const T0& op0, const T1& op1) 670 { 671 DE_UNREF(op0); 672 DE_UNREF(op1); 673 return shaderstream; 674 } 675 676 // Specialisation for Operation being 1st parameter 677 // Certain operations make the SPIRV code line to be pushed to different substreams. 678 template <typename T1> 679 std::stringstream& selectStream (const Operation& op, const T1& op1) 680 { 681 DE_UNREF(op1); 682 if (op == op::Decorate || op == op::MemberDecorate) 683 return decorations; 684 if (op == op::Name || op == op::MemberName) 685 return names; 686 if (op == op::Capability || op == op::Extension) 687 return capabilities; 688 if (op == op::MemoryModel || op == op::ExecutionMode || op == op::EntryPoint) 689 return preamble; 690 return shaderstream; 691 } 692 693 // Specialisation for Operation being 3rd parameter 694 // Certain operations make the SPIRV code line to be pushed to different substreams. 695 // If we would like to use this way of generating SPIRV we could use this method as SPIRV line validation point 696 // e.g. here instead of heving partial specialisation I could specialise for T0 being Variable since this has to match Variable = Operation operands... 697 template <typename T0> 698 std::stringstream& selectStream (const T0& op0, const Operation& op) 699 { 700 DE_UNREF(op0); 701 if (op == op::ExtInstImport) 702 return preamble; 703 if (op == op::TypeVoid || op == op::TypeBool || op == op::TypeInt || op == op::TypeFloat || op == op::TypeVector || op == op::TypeMatrix) 704 return basictypes; 705 if (op == op::TypeArray || op == op::TypeStruct || op == op::TypeFunction || op == op::TypePointer || op == op::TypeImage || op == op::TypeSampledImage) 706 return compositetypes; 707 if (op == op::Constant) 708 return constants; 709 if (op == op::ConstantComposite) 710 return compositeconstants; 711 return shaderstream; 712 } 713 714 typedef std::map<Variable, std::string> VariablesPack; 715 typedef VariablesPack::iterator VariableIt; 716 717 // local mappings between c++ Variable objects and in-SPIRV-code names 718 VariablesPack vars; 719 720 // shader substreams 721 std::stringstream capabilities; 722 std::stringstream preamble; 723 std::stringstream names; 724 std::stringstream decorations; 725 std::stringstream basictypes; 726 std::stringstream constants; 727 std::stringstream compositetypes; 728 std::stringstream compositeconstants; 729 std::stringstream shaderstream; 730 731 // local incremented counter 732 Autocounter varCounter; 733 }; 734 735 // A suppliementary class to group frequently used Variables together 736 class Variables 737 { 738 public: 739 Variables (Autocounter &autoincrement) 740 : version(autoincrement) 741 , mainFunc(autoincrement) 742 , mainFuncLabel(autoincrement) 743 , voidFuncVoid(autoincrement) 744 , copy_type(autoincrement) 745 , copy_type_vec(autoincrement) 746 , buffer_type_vec(autoincrement) 747 , copy_type_ptr(autoincrement) 748 , buffer_type(autoincrement) 749 , voidId(autoincrement) 750 , v4f32(autoincrement) 751 , v4s32(autoincrement) 752 , v4u32(autoincrement) 753 , s32(autoincrement) 754 , f32(autoincrement) 755 , u32(autoincrement) 756 , boolean(autoincrement) 757 , array_content_type(autoincrement) 758 , s32_type_ptr(autoincrement) 759 , dataSelectorStructPtrType(autoincrement) 760 , dataSelectorStructPtr(autoincrement) 761 , dataArrayType(autoincrement) 762 , dataInput(autoincrement) 763 , dataInputPtrType(autoincrement) 764 , dataInputType(autoincrement) 765 , dataInputSampledType(autoincrement) 766 , dataOutput(autoincrement) 767 , dataOutputPtrType(autoincrement) 768 , dataOutputType(autoincrement) 769 , dataSelectorStructType(autoincrement) 770 , input(autoincrement) 771 , inputPtr(autoincrement) 772 , output(autoincrement) 773 , outputPtr(autoincrement) 774 { 775 for (deUint32 i = 0; i < 32; ++i) 776 constants.push_back(Variable(autoincrement)); 777 } 778 const Variable version; 779 const Variable mainFunc; 780 const Variable mainFuncLabel; 781 const Variable voidFuncVoid; 782 std::vector<Variable> constants; 783 const Variable copy_type; 784 const Variable copy_type_vec; 785 const Variable buffer_type_vec; 786 const Variable copy_type_ptr; 787 const Variable buffer_type; 788 const Variable voidId; 789 const Variable v4f32; 790 const Variable v4s32; 791 const Variable v4u32; 792 const Variable s32; 793 const Variable f32; 794 const Variable u32; 795 const Variable boolean; 796 const Variable array_content_type; 797 const Variable s32_type_ptr; 798 const Variable dataSelectorStructPtrType; 799 const Variable dataSelectorStructPtr; 800 const Variable dataArrayType; 801 const Variable dataInput; 802 const Variable dataInputPtrType; 803 const Variable dataInputType; 804 const Variable dataInputSampledType; 805 const Variable dataOutput; 806 const Variable dataOutputPtrType; 807 const Variable dataOutputType; 808 const Variable dataSelectorStructType; 809 const Variable input; 810 const Variable inputPtr; 811 const Variable output; 812 const Variable outputPtr; 813 }; 814 815 // A routing generating SPIRV code for all test cases in this group 816 std::string MakeShader(VkShaderStageFlags shaderStage, ShaderType shaderType, VkFormat bufferFormat, bool reads, bool dummy) 817 { 818 // faster to write 819 const char is = '='; 820 821 // variables require such counter to generate their unique ids. Since there is possibility that in the future this code will 822 // run parallel this counter is made local to this function body to be safe. 823 Autocounter localcounter; 824 825 // A frequently used Variables (gathered into this single object for readability) 826 Variables var (localcounter); 827 828 // A SPIRV code builder 829 ShaderStream shaderSource; 830 831 // A basic preamble of SPIRV shader. Turns on required capabilities and extensions. 832 shaderSource 833 (op::Capability, "Shader") 834 (op::Capability, "VariablePointersStorageBuffer") 835 (op::Extension, "\"SPV_KHR_storage_buffer_storage_class\"") 836 (op::Extension, "\"SPV_KHR_variable_pointers\"") 837 (var.version, is, op::ExtInstImport, "\"GLSL.std.450\"") 838 (op::MemoryModel, "Logical", "GLSL450"); 839 840 // Use correct entry point definition depending on shader stage 841 if (shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) 842 { 843 shaderSource 844 (op::EntryPoint, "GLCompute", var.mainFunc, "\"main\"") 845 (op::ExecutionMode, var.mainFunc, "LocalSize", 1, 1, 1); 846 } 847 else if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) 848 { 849 shaderSource 850 (op::EntryPoint, "Vertex", var.mainFunc, "\"main\"", var.input, var.output) 851 (op::Decorate, var.output, "BuiltIn", "Position") 852 (op::Decorate, var.input, "Location", 0); 853 } 854 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 855 { 856 shaderSource 857 (op::EntryPoint, "Fragment", var.mainFunc, "\"main\"", var.output) 858 (op::ExecutionMode, var.mainFunc, "OriginUpperLeft") 859 (op::Decorate, var.output, "Location", 0); 860 } 861 862 // If we are testing vertex shader or fragment shader we need to provide the other one for the pipeline too. 863 // So the not tested one is 'dummy'. It is then a minimal/simplest possible pass-through shader. 864 // If we are testing compute shader we dont need dummy shader at all. 865 if (dummy) 866 { 867 if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 868 { 869 shaderSource 870 (var.voidId, is, op::TypeVoid) 871 (var.voidFuncVoid, is, op::TypeFunction, var.voidId) 872 (var.f32, is, op::TypeFloat, 32) 873 (var.v4f32, is, op::TypeVector, var.f32, 4) 874 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32) 875 (var.output, is, op::Variable, var.outputPtr, "Output") 876 (var.constants[6], is, op::Constant, var.f32, 1) 877 (var.constants[7], is, op::ConstantComposite, var.v4f32, var.constants[6], var.constants[6], var.constants[6], var.constants[6]) 878 (var.mainFunc, is, op::Function, var.voidId, "None", var.voidFuncVoid) 879 (var.mainFuncLabel, is, op::Label); 880 } 881 else if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) 882 { 883 shaderSource 884 (var.voidId, is, op::TypeVoid) 885 (var.voidFuncVoid, is, op::TypeFunction , var.voidId) 886 (var.f32, is, op::TypeFloat, 32) 887 (var.v4f32, is, op::TypeVector , var.f32, 4) 888 (var.outputPtr, is, op::TypePointer, "Output" , var.v4f32) 889 (var.output, is, op::Variable , var.outputPtr, "Output") 890 (var.inputPtr, is, op::TypePointer, "Input" , var.v4f32) 891 (var.input, is, op::Variable , var.inputPtr, "Input") 892 (var.mainFunc, is, op::Function , var.voidId, "None", var.voidFuncVoid) 893 (var.mainFuncLabel, is, op::Label); 894 } 895 } 896 else // this is a start of actual shader that tests variable pointers 897 { 898 shaderSource 899 (op::Decorate, var.dataInput, "DescriptorSet", 0) 900 (op::Decorate, var.dataInput, "Binding", 0) 901 902 (op::Decorate, var.dataOutput, "DescriptorSet", 0) 903 (op::Decorate, var.dataOutput, "Binding", 1); 904 905 // for scalar types and vector types we use 1024 element array of 4 elements arrays of 4-component vectors 906 // so the stride of internal array is size of 4-component vector 907 if (shaderType == SHADER_TYPE_SCALAR_COPY || shaderType == SHADER_TYPE_VECTOR_COPY) 908 { 909 shaderSource 910 (op::Decorate, var.array_content_type, "ArrayStride", 16); 911 } 912 // for matrices we use array of 4x4-component matrices 913 // stride of outer array is then 64 in every case 914 shaderSource 915 (op::Decorate, var.dataArrayType, "ArrayStride", 64) 916 917 // an output block 918 (op::MemberDecorate, var.dataOutputType, 0, "Offset", 0) 919 (op::Decorate, var.dataOutputType, "Block") 920 921 // an input block. Marked readonly. 922 (op::MemberDecorate, var.dataInputType, 0, "NonWritable") 923 (op::MemberDecorate, var.dataInputType, 0, "Offset", 0) 924 (op::Decorate, var.dataInputType, "Block") 925 926 //a special structure matching data in one of our buffers. 927 // member at 0 is an index to read position 928 // member at 1 is an index to write position 929 // member at 2 is always zero. It is used to perform OpSelect. I used value coming from buffer to avoid incidental optimisations that could prune OpSelect if the value was compile time known. 930 (op::MemberDecorate, var.dataSelectorStructType, 0, "Offset", 0) 931 (op::MemberDecorate, var.dataSelectorStructType, 1, "Offset", 4) 932 (op::MemberDecorate, var.dataSelectorStructType, 2, "Offset", 8) 933 (op::Decorate, var.dataSelectorStructType, "Block") 934 935 // binding to matching buffer 936 (op::Decorate, var.dataSelectorStructPtr, "DescriptorSet", 0) 937 (op::Decorate, var.dataSelectorStructPtr, "Binding", 2) 938 939 // making composite types used in shader 940 (var.voidId, is, op::TypeVoid) 941 (var.voidFuncVoid, is, op::TypeFunction, var.voidId) 942 943 (var.boolean, is, op::TypeBool) 944 945 (var.f32, is, op::TypeFloat, 32) 946 (var.s32, is, op::TypeInt, 32, 1) 947 (var.u32, is, op::TypeInt, 32, 0) 948 949 (var.v4f32, is, op::TypeVector, var.f32, 4) 950 (var.v4s32, is, op::TypeVector, var.s32, 4) 951 (var.v4u32, is, op::TypeVector, var.u32, 4); 952 953 // since the shared tests scalars, vectors, matrices of ints, uints and floats I am generating alternative names for some of the types so I can use those and not need to use "if" everywhere. 954 // A Variable mappings will make sure the proper variable name is used 955 // below is a first part of aliasing types based on int, uint, float 956 switch (bufferFormat) 957 { 958 case vk::VK_FORMAT_R32_SINT: 959 shaderSource.makeSame(var.buffer_type, var.s32); 960 shaderSource.makeSame(var.buffer_type_vec, var.v4s32); 961 break; 962 case vk::VK_FORMAT_R32_UINT: 963 shaderSource.makeSame(var.buffer_type, var.u32); 964 shaderSource.makeSame(var.buffer_type_vec, var.v4u32); 965 break; 966 case vk::VK_FORMAT_R32_SFLOAT: 967 shaderSource.makeSame(var.buffer_type, var.f32); 968 shaderSource.makeSame(var.buffer_type_vec, var.v4f32); 969 break; 970 default: 971 // to prevent compiler from complaining not all cases are handled (but we should not get here). 972 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__); 973 break; 974 } 975 976 // below is a second part that aliases based on scalar, vector, matrix 977 switch (shaderType) 978 { 979 case SHADER_TYPE_SCALAR_COPY: 980 shaderSource.makeSame(var.copy_type, var.buffer_type); 981 break; 982 case SHADER_TYPE_VECTOR_COPY: 983 shaderSource.makeSame(var.copy_type, var.buffer_type_vec); 984 break; 985 case SHADER_TYPE_MATRIX_COPY: 986 if (bufferFormat != VK_FORMAT_R32_SFLOAT) 987 TCU_THROW(NotSupportedError, "Matrices can be used only with floating point types."); 988 shaderSource 989 (var.copy_type, is, op::TypeMatrix, var.buffer_type_vec, 4); 990 break; 991 default: 992 // to prevent compiler from complaining not all cases are handled (but we should not get here). 993 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__); 994 break; 995 } 996 997 // I will need some constants so lets add them to shader source 998 shaderSource 999 (var.constants[0], is, op::Constant, var.s32, 0) 1000 (var.constants[1], is, op::Constant, var.s32, 1) 1001 (var.constants[2], is, op::Constant, var.s32, 2) 1002 (var.constants[3], is, op::Constant, var.s32, 3) 1003 (var.constants[4], is, op::Constant, var.u32, 4) 1004 (var.constants[5], is, op::Constant, var.u32, 1024); 1005 1006 // for fragment shaders I need additionally a constant vector (output "colour") so lets make it 1007 if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 1008 { 1009 shaderSource 1010 (var.constants[6], is, op::Constant, var.f32, 1) 1011 (var.constants[7], is, op::ConstantComposite, var.v4f32, var.constants[6], var.constants[6], var.constants[6], var.constants[6]); 1012 } 1013 1014 // additional alias for the type of content of this 1024-element outer array. 1015 if (shaderType == SHADER_TYPE_SCALAR_COPY || shaderType == SHADER_TYPE_VECTOR_COPY) 1016 { 1017 shaderSource 1018 (var.array_content_type, is, op::TypeArray, var.buffer_type_vec, var.constants[4]); 1019 } 1020 else 1021 { 1022 shaderSource.makeSame(var.array_content_type, var.copy_type); 1023 } 1024 1025 // Lets create pointer types to the input data type, output data type and a struct 1026 // This must be distinct types due to different type decorations 1027 // Lets make also actual poiters to the data 1028 shaderSource 1029 (var.dataArrayType, is, op::TypeArray, var.array_content_type, var.constants[5]) 1030 (var.dataInputType, is, op::TypeStruct, var.dataArrayType) 1031 (var.dataOutputType, is, op::TypeStruct, var.dataArrayType) 1032 (var.dataInputPtrType, is, op::TypePointer, "StorageBuffer", var.dataInputType) 1033 (var.dataOutputPtrType, is, op::TypePointer, "StorageBuffer", var.dataOutputType) 1034 (var.dataInput, is, op::Variable, var.dataInputPtrType, "StorageBuffer") 1035 (var.dataOutput, is, op::Variable, var.dataOutputPtrType, "StorageBuffer") 1036 (var.dataSelectorStructType, is, op::TypeStruct, var.s32, var.s32, var.s32) 1037 (var.dataSelectorStructPtrType, is, op::TypePointer, "Uniform", var.dataSelectorStructType) 1038 (var.dataSelectorStructPtr, is, op::Variable, var.dataSelectorStructPtrType, "Uniform"); 1039 1040 // we need also additional pointers to fullfil stage requirements on shaders inputs and outputs 1041 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) 1042 { 1043 shaderSource 1044 (var.inputPtr, is, op::TypePointer, "Input", var.v4f32) 1045 (var.input, is, op::Variable, var.inputPtr, "Input") 1046 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32) 1047 (var.output, is, op::Variable, var.outputPtr, "Output"); 1048 } 1049 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 1050 { 1051 shaderSource 1052 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32) 1053 (var.output, is, op::Variable, var.outputPtr, "Output"); 1054 } 1055 1056 shaderSource 1057 (var.copy_type_ptr, is, op::TypePointer, "StorageBuffer", var.copy_type) 1058 (var.s32_type_ptr, is, op::TypePointer, "Uniform", var.s32); 1059 1060 // Make a shader main function 1061 shaderSource 1062 (var.mainFunc, is, op::Function, var.voidId, "None", var.voidFuncVoid) 1063 (var.mainFuncLabel, is, op::Label); 1064 1065 Variable copyFromPtr(localcounter), copyToPtr(localcounter), zeroPtr(localcounter); 1066 Variable copyFrom(localcounter), copyTo(localcounter), zero(localcounter); 1067 1068 // Lets load data from our auxiliary buffer with reading index, writing index and zero. 1069 shaderSource 1070 (copyToPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[1]) 1071 (copyTo, is, op::Load, var.s32, copyToPtr) 1072 (copyFromPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[0]) 1073 (copyFrom, is, op::Load, var.s32, copyFromPtr) 1074 (zeroPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[2]) 1075 (zero, is, op::Load, var.s32, zeroPtr); 1076 1077 // let start copying data using variable pointers 1078 switch (shaderType) 1079 { 1080 case SHADER_TYPE_SCALAR_COPY: 1081 for (int i = 0; i < 4; ++i) 1082 { 1083 for (int j = 0; j < 4; ++j) 1084 { 1085 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter); 1086 Variable selection(localcounter); 1087 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter); 1088 1089 shaderSource 1090 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]); 1091 1092 if (reads) 1093 { 1094 // if we check reads we use variable pointers only for reading part 1095 shaderSource 1096 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j]) 1097 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j]) 1098 // actualLoadChain will be a variable pointer as it was created through OpSelect 1099 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB) 1100 // actualStoreChain will be a regular pointer 1101 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j]); 1102 } 1103 else 1104 { 1105 // if we check writes we use variable pointers only for writing part only 1106 shaderSource 1107 // actualLoadChain will be regular regualar pointer 1108 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j]) 1109 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j]) 1110 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j]) 1111 // actualStoreChain will be a variable pointer as it was created through OpSelect 1112 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB); 1113 } 1114 // do actual copying 1115 shaderSource 1116 (loadResult, is, op::Load, var.copy_type, actualLoadChain) 1117 (op::Store, actualStoreChain, loadResult); 1118 } 1119 } 1120 break; 1121 // cases below have the same logic as the one above - just we are copying bigger chunks of data with every load/store pair 1122 case SHADER_TYPE_VECTOR_COPY: 1123 for (int i = 0; i < 4; ++i) 1124 { 1125 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter); 1126 Variable selection(localcounter); 1127 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter); 1128 1129 shaderSource 1130 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]); 1131 1132 if (reads) 1133 { 1134 shaderSource 1135 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i]) 1136 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i]) 1137 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB) 1138 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i]); 1139 } 1140 else 1141 { 1142 shaderSource 1143 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i]) 1144 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i]) 1145 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i]) 1146 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB); 1147 } 1148 1149 shaderSource 1150 (loadResult, is, op::Load, var.copy_type, actualLoadChain) 1151 (op::Store, actualStoreChain, loadResult); 1152 } 1153 break; 1154 case SHADER_TYPE_MATRIX_COPY: 1155 { 1156 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter); 1157 Variable selection(localcounter); 1158 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter); 1159 1160 shaderSource 1161 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]); 1162 1163 if (reads) 1164 { 1165 shaderSource 1166 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom) 1167 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom) 1168 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB) 1169 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo); 1170 } 1171 else 1172 { 1173 shaderSource 1174 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom) 1175 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo) 1176 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo) 1177 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB); 1178 } 1179 1180 shaderSource 1181 (loadResult, is, op::Load, var.copy_type, actualLoadChain) 1182 (op::Store, actualStoreChain, loadResult); 1183 } 1184 break; 1185 default: 1186 // to prevent compiler from complaining not all cases are handled (but we should not get here). 1187 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__); 1188 break; 1189 } 1190 } 1191 1192 // This is common for test shaders and dummy ones 1193 // We need to fill stage ouput from shader properly 1194 // output vertices positions in vertex shader 1195 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) 1196 { 1197 Variable inputValue(localcounter), outputLocation(localcounter); 1198 shaderSource 1199 (inputValue, is, op::Load, var.v4f32, var.input) 1200 (outputLocation, is, op::AccessChain, var.outputPtr, var.output) 1201 (op::Store, outputLocation, inputValue); 1202 } 1203 // output colour in fragment shader 1204 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 1205 { 1206 shaderSource 1207 (op::Store, var.output, var.constants[7]); 1208 } 1209 1210 // We are done. Lets close main function body 1211 shaderSource 1212 (op::Return) 1213 (op::FunctionEnd); 1214 1215 return shaderSource.str(); 1216 } 1217 1218 RobustReadTest::RobustReadTest (tcu::TestContext& testContext, 1219 const std::string& name, 1220 const std::string& description, 1221 VkShaderStageFlags shaderStage, 1222 ShaderType shaderType, 1223 VkFormat bufferFormat, 1224 VkDeviceSize readAccessRange, 1225 bool accessOutOfBackingMemory) 1226 : RobustAccessWithPointersTest (testContext, name, description, shaderStage, shaderType, bufferFormat) 1227 , m_readAccessRange (readAccessRange) 1228 , m_accessOutOfBackingMemory (accessOutOfBackingMemory) 1229 { 1230 } 1231 1232 TestInstance* RobustReadTest::createInstance (Context& context) const 1233 { 1234 VkPhysicalDeviceVariablePointerFeatures pointerFeatures = querySupportedVariablePointersFeatures(context.getUsedApiVersion(), context.getInstanceInterface(), context.getPhysicalDevice(), context.getInstanceExtensions()); 1235 1236 if (pointerFeatures.variablePointersStorageBuffer != DE_TRUE) 1237 return new NotSupportedInstance(context, std::string("VariablePointersStorageBuffer support is required for this test.")); 1238 1239 // We need a device with enabled robust buffer access feature (it is disabled in default device) 1240 Move<VkDevice> device = createRobustBufferAccessDevice(context); 1241 return new ReadInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_readAccessRange, m_accessOutOfBackingMemory); 1242 } 1243 1244 void RobustReadTest::initPrograms(SourceCollections& programCollection) const 1245 { 1246 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) 1247 { 1248 programCollection.spirvAsmSources.add("compute") << MakeShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaderType, m_bufferFormat, true, false); 1249 } 1250 else 1251 { 1252 programCollection.spirvAsmSources.add("vertex") << MakeShader(VK_SHADER_STAGE_VERTEX_BIT, m_shaderType, m_bufferFormat, true, m_shaderStage != VK_SHADER_STAGE_VERTEX_BIT); 1253 programCollection.spirvAsmSources.add("fragment") << MakeShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_shaderType, m_bufferFormat, true, m_shaderStage != VK_SHADER_STAGE_FRAGMENT_BIT); 1254 } 1255 } 1256 1257 RobustWriteTest::RobustWriteTest (tcu::TestContext& testContext, 1258 const std::string& name, 1259 const std::string& description, 1260 VkShaderStageFlags shaderStage, 1261 ShaderType shaderType, 1262 VkFormat bufferFormat, 1263 VkDeviceSize writeAccessRange, 1264 bool accessOutOfBackingMemory) 1265 1266 : RobustAccessWithPointersTest (testContext, name, description, shaderStage, shaderType, bufferFormat) 1267 , m_writeAccessRange (writeAccessRange) 1268 , m_accessOutOfBackingMemory (accessOutOfBackingMemory) 1269 { 1270 } 1271 1272 TestInstance* RobustWriteTest::createInstance (Context& context) const 1273 { 1274 VkPhysicalDeviceVariablePointerFeatures pointerFeatures = querySupportedVariablePointersFeatures(context.getUsedApiVersion(), context.getInstanceInterface(), context.getPhysicalDevice(), context.getInstanceExtensions()); 1275 if (pointerFeatures.variablePointersStorageBuffer != DE_TRUE) 1276 return new NotSupportedInstance(context, std::string("VariablePointersStorageBuffer support is required for this test.")); 1277 1278 // We need a device with enabled robust buffer access feature (it is disabled in default device) 1279 Move<VkDevice> device = createRobustBufferAccessDevice(context); 1280 return new WriteInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_writeAccessRange, m_accessOutOfBackingMemory); 1281 } 1282 1283 void RobustWriteTest::initPrograms(SourceCollections& programCollection) const 1284 { 1285 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) 1286 { 1287 programCollection.spirvAsmSources.add("compute") << MakeShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaderType, m_bufferFormat, false, false); 1288 } 1289 else 1290 { 1291 programCollection.spirvAsmSources.add("vertex") << MakeShader(VK_SHADER_STAGE_VERTEX_BIT, m_shaderType, m_bufferFormat, false, m_shaderStage != VK_SHADER_STAGE_VERTEX_BIT); 1292 programCollection.spirvAsmSources.add("fragment") << MakeShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_shaderType, m_bufferFormat, false, m_shaderStage != VK_SHADER_STAGE_FRAGMENT_BIT); 1293 } 1294 } 1295 1296 AccessInstance::AccessInstance (Context& context, 1297 Move<VkDevice> device, 1298 ShaderType shaderType, 1299 VkShaderStageFlags shaderStage, 1300 VkFormat bufferFormat, 1301 BufferAccessType bufferAccessType, 1302 VkDeviceSize inBufferAccessRange, 1303 VkDeviceSize outBufferAccessRange, 1304 bool accessOutOfBackingMemory) 1305 : vkt::TestInstance (context) 1306 , m_device (device) 1307 , m_shaderType (shaderType) 1308 , m_shaderStage (shaderStage) 1309 , m_bufferFormat (bufferFormat) 1310 , m_bufferAccessType (bufferAccessType) 1311 , m_accessOutOfBackingMemory (accessOutOfBackingMemory) 1312 { 1313 tcu::TestLog& log = context.getTestContext().getLog(); 1314 const DeviceInterface& vk = context.getDeviceInterface(); 1315 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); 1316 SimpleAllocator memAlloc (vk, *m_device, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice())); 1317 1318 DE_ASSERT(RobustAccessWithPointersTest::s_numberOfBytesAccessed % sizeof(deUint32) == 0); 1319 DE_ASSERT(inBufferAccessRange <= RobustAccessWithPointersTest::s_numberOfBytesAccessed); 1320 DE_ASSERT(outBufferAccessRange <= RobustAccessWithPointersTest::s_numberOfBytesAccessed); 1321 1322 // Check storage support 1323 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT) 1324 { 1325 if (!context.getDeviceFeatures().vertexPipelineStoresAndAtomics) 1326 { 1327 TCU_THROW(NotSupportedError, "Stores not supported in vertex stage"); 1328 } 1329 } 1330 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) 1331 { 1332 if (!context.getDeviceFeatures().fragmentStoresAndAtomics) 1333 { 1334 TCU_THROW(NotSupportedError, "Stores not supported in fragment stage"); 1335 } 1336 } 1337 1338 createTestBuffer(vk, *m_device, inBufferAccessRange, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memAlloc, m_inBuffer, m_inBufferAlloc, m_inBufferAccess, &populateBufferWithValues, &m_bufferFormat); 1339 createTestBuffer(vk, *m_device, outBufferAccessRange, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memAlloc, m_outBuffer, m_outBufferAlloc, m_outBufferAccess, &populateBufferWithDummy, DE_NULL); 1340 1341 deInt32 indices[] = { 1342 (m_accessOutOfBackingMemory && (m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE)) ? static_cast<deInt32>(RobustAccessWithPointersTest::s_testArraySize) - 1 : 0, 1343 (m_accessOutOfBackingMemory && (m_bufferAccessType == BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE)) ? static_cast<deInt32>(RobustAccessWithPointersTest::s_testArraySize) - 1 : 0, 1344 0 1345 }; 1346 AccessRangesData indicesAccess; 1347 createTestBuffer(vk, *m_device, 3 * sizeof(deInt32), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, memAlloc, m_indicesBuffer, m_indicesBufferAlloc, indicesAccess, &populateBufferWithCopy, &indices); 1348 1349 log << tcu::TestLog::Message << "input buffer - alloc size: " << m_inBufferAccess.allocSize << tcu::TestLog::EndMessage; 1350 log << tcu::TestLog::Message << "input buffer - max access range: " << m_inBufferAccess.maxAccessRange << tcu::TestLog::EndMessage; 1351 log << tcu::TestLog::Message << "output buffer - alloc size: " << m_outBufferAccess.allocSize << tcu::TestLog::EndMessage; 1352 log << tcu::TestLog::Message << "output buffer - max access range: " << m_outBufferAccess.maxAccessRange << tcu::TestLog::EndMessage; 1353 log << tcu::TestLog::Message << "indices - input offset: " << indices[0] << tcu::TestLog::EndMessage; 1354 log << tcu::TestLog::Message << "indices - output offset: " << indices[1] << tcu::TestLog::EndMessage; 1355 log << tcu::TestLog::Message << "indices - additional: " << indices[2] << tcu::TestLog::EndMessage; 1356 1357 // Create descriptor data 1358 { 1359 DescriptorPoolBuilder descriptorPoolBuilder; 1360 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u); 1361 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u); 1362 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u); 1363 m_descriptorPool = descriptorPoolBuilder.build(vk, *m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); 1364 1365 DescriptorSetLayoutBuilder setLayoutBuilder; 1366 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_ALL); 1367 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_ALL); 1368 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL); 1369 m_descriptorSetLayout = setLayoutBuilder.build(vk, *m_device); 1370 1371 const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = 1372 { 1373 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType; 1374 DE_NULL, // const void* pNext; 1375 *m_descriptorPool, // VkDescriptorPool descriptorPool; 1376 1u, // deUint32 setLayoutCount; 1377 &m_descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts; 1378 }; 1379 1380 m_descriptorSet = allocateDescriptorSet(vk, *m_device, &descriptorSetAllocateInfo); 1381 1382 const VkDescriptorBufferInfo inBufferDescriptorInfo = makeDescriptorBufferInfo(*m_inBuffer, 0ull, m_inBufferAccess.accessRange); 1383 const VkDescriptorBufferInfo outBufferDescriptorInfo = makeDescriptorBufferInfo(*m_outBuffer, 0ull, m_outBufferAccess.accessRange); 1384 const VkDescriptorBufferInfo indicesBufferDescriptorInfo = makeDescriptorBufferInfo(*m_indicesBuffer, 0ull, 12ull); 1385 1386 DescriptorSetUpdateBuilder setUpdateBuilder; 1387 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &inBufferDescriptorInfo); 1388 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outBufferDescriptorInfo); 1389 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &indicesBufferDescriptorInfo); 1390 setUpdateBuilder.update(vk, *m_device); 1391 } 1392 1393 // Create fence 1394 { 1395 const VkFenceCreateInfo fenceParams = 1396 { 1397 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType; 1398 DE_NULL, // const void* pNext; 1399 0u // VkFenceCreateFlags flags; 1400 }; 1401 1402 m_fence = createFence(vk, *m_device, &fenceParams); 1403 } 1404 1405 // Get queue 1406 vk.getDeviceQueue(*m_device, queueFamilyIndex, 0, &m_queue); 1407 1408 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT) 1409 { 1410 m_testEnvironment = de::MovePtr<TestEnvironment>(new ComputeEnvironment(m_context, *m_device, *m_descriptorSetLayout, *m_descriptorSet)); 1411 } 1412 else 1413 { 1414 using tcu::Vec4; 1415 1416 const VkVertexInputBindingDescription vertexInputBindingDescription = 1417 { 1418 0u, // deUint32 binding; 1419 sizeof(tcu::Vec4), // deUint32 strideInBytes; 1420 VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate; 1421 }; 1422 1423 const VkVertexInputAttributeDescription vertexInputAttributeDescription = 1424 { 1425 0u, // deUint32 location; 1426 0u, // deUint32 binding; 1427 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; 1428 0u // deUint32 offset; 1429 }; 1430 1431 AccessRangesData vertexAccess; 1432 const Vec4 vertices[] = 1433 { 1434 Vec4(-1.0f, -1.0f, 0.0f, 1.0f), 1435 Vec4(-1.0f, 1.0f, 0.0f, 1.0f), 1436 Vec4( 1.0f, -1.0f, 0.0f, 1.0f), 1437 }; 1438 const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(sizeof(vertices)); 1439 createTestBuffer(vk, *m_device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, memAlloc, m_vertexBuffer, m_vertexBufferAlloc, vertexAccess, &populateBufferWithCopy, &vertices); 1440 1441 const GraphicsEnvironment::DrawConfig drawWithOneVertexBuffer = 1442 { 1443 std::vector<VkBuffer>(1, *m_vertexBuffer), // std::vector<VkBuffer> vertexBuffers; 1444 DE_LENGTH_OF_ARRAY(vertices), // deUint32 vertexCount; 1445 1, // deUint32 instanceCount; 1446 DE_NULL, // VkBuffer indexBuffer; 1447 0u, // deUint32 indexCount; 1448 }; 1449 1450 m_testEnvironment = de::MovePtr<TestEnvironment>(new GraphicsEnvironment(m_context, 1451 *m_device, 1452 *m_descriptorSetLayout, 1453 *m_descriptorSet, 1454 GraphicsEnvironment::VertexBindings(1, vertexInputBindingDescription), 1455 GraphicsEnvironment::VertexAttributes(1, vertexInputAttributeDescription), 1456 drawWithOneVertexBuffer)); 1457 } 1458 } 1459 1460 // Verifies if the buffer has the value initialized by BufferAccessInstance::populateReadBuffer at a given offset. 1461 bool AccessInstance::isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, 1462 const void* valuePtr, 1463 VkDeviceSize valueSize) 1464 { 1465 DE_ASSERT(offsetInBytes % 4 == 0); 1466 DE_ASSERT(offsetInBytes < m_inBufferAccess.allocSize); 1467 1468 const deUint32 valueIndex = deUint32(offsetInBytes / 4) + 2; 1469 1470 if (isUintFormat(m_bufferFormat)) 1471 { 1472 return !deMemCmp(valuePtr, &valueIndex, (size_t)valueSize); 1473 } 1474 else if (isIntFormat(m_bufferFormat)) 1475 { 1476 const deInt32 value = -deInt32(valueIndex); 1477 return !deMemCmp(valuePtr, &value, (size_t)valueSize); 1478 } 1479 else if (isFloatFormat(m_bufferFormat)) 1480 { 1481 const float value = float(valueIndex); 1482 return !deMemCmp(valuePtr, &value, (size_t)valueSize); 1483 } 1484 else 1485 { 1486 DE_ASSERT(false); 1487 return false; 1488 } 1489 } 1490 1491 bool AccessInstance::isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize) 1492 { 1493 DE_ASSERT(valueSize <= 4); 1494 const deUint8 *const outValuePtr = (deUint8*)m_outBufferAlloc->getHostPtr() + offsetInBytes; 1495 const deUint32 defaultValue = 0xBABABABAu; 1496 1497 return !deMemCmp(outValuePtr, &defaultValue, (size_t)valueSize); 1498 } 1499 1500 tcu::TestStatus AccessInstance::iterate (void) 1501 { 1502 const DeviceInterface& vk = m_context.getDeviceInterface(); 1503 const vk::VkCommandBuffer cmdBuffer = m_testEnvironment->getCommandBuffer(); 1504 1505 // Submit command buffer 1506 { 1507 const VkSubmitInfo submitInfo = 1508 { 1509 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; 1510 DE_NULL, // const void* pNext; 1511 0u, // deUint32 waitSemaphoreCount; 1512 DE_NULL, // const VkSemaphore* pWaitSemaphores; 1513 DE_NULL, // const VkPIpelineStageFlags* pWaitDstStageMask; 1514 1u, // deUint32 commandBufferCount; 1515 &cmdBuffer, // const VkCommandBuffer* pCommandBuffers; 1516 0u, // deUint32 signalSemaphoreCount; 1517 DE_NULL // const VkSemaphore* pSignalSemaphores; 1518 }; 1519 1520 VK_CHECK(vk.resetFences(*m_device, 1, &m_fence.get())); 1521 VK_CHECK(vk.queueSubmit(m_queue, 1, &submitInfo, *m_fence)); 1522 VK_CHECK(vk.waitForFences(*m_device, 1, &m_fence.get(), true, ~(0ull) /* infinity */)); 1523 } 1524 1525 // Prepare result buffer for read 1526 { 1527 const VkMappedMemoryRange outBufferRange = 1528 { 1529 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType; 1530 DE_NULL, // const void* pNext; 1531 m_outBufferAlloc->getMemory(), // VkDeviceMemory mem; 1532 0ull, // VkDeviceSize offset; 1533 m_outBufferAccess.allocSize, // VkDeviceSize size; 1534 }; 1535 1536 VK_CHECK(vk.invalidateMappedMemoryRanges(*m_device, 1u, &outBufferRange)); 1537 } 1538 1539 if (verifyResult()) 1540 return tcu::TestStatus::pass("All values OK"); 1541 else 1542 return tcu::TestStatus::fail("Invalid value(s) found"); 1543 } 1544 1545 bool AccessInstance::verifyResult (void) 1546 { 1547 std::ostringstream logMsg; 1548 tcu::TestLog& log = m_context.getTestContext().getLog(); 1549 const bool isReadAccess = (m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE); 1550 const void* inDataPtr = m_inBufferAlloc->getHostPtr(); 1551 const void* outDataPtr = m_outBufferAlloc->getHostPtr(); 1552 bool allOk = true; 1553 deUint32 valueNdx = 0; 1554 const VkDeviceSize maxAccessRange = isReadAccess ? m_inBufferAccess.maxAccessRange : m_outBufferAccess.maxAccessRange; 1555 1556 for (VkDeviceSize offsetInBytes = 0; offsetInBytes < m_outBufferAccess.allocSize; offsetInBytes += 4) 1557 { 1558 const deUint8* outValuePtr = static_cast<const deUint8*>(outDataPtr) + offsetInBytes; 1559 const size_t outValueSize = static_cast<size_t>(deMinu64(4, (m_outBufferAccess.allocSize - offsetInBytes))); 1560 1561 if (offsetInBytes >= RobustAccessWithPointersTest::s_numberOfBytesAccessed) 1562 { 1563 // The shader will only write 16 values into the result buffer. The rest of the values 1564 // should remain unchanged or may be modified if we are writing out of bounds. 1565 if (!isOutBufferValueUnchanged(offsetInBytes, outValueSize) 1566 && (isReadAccess || !isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, 4))) 1567 { 1568 logMsg << "\nValue " << valueNdx++ << " has been modified with an unknown value: " << *(static_cast<const deUint32*>(static_cast<const void*>(outValuePtr))); 1569 allOk = false; 1570 } 1571 } 1572 else 1573 { 1574 const deInt32 distanceToOutOfBounds = static_cast<deInt32>(maxAccessRange) - static_cast<deInt32>(offsetInBytes); 1575 bool isOutOfBoundsAccess = false; 1576 1577 logMsg << "\n" << valueNdx++ << ": "; 1578 1579 logValue(logMsg, outValuePtr, m_bufferFormat, outValueSize); 1580 1581 if (m_accessOutOfBackingMemory) 1582 isOutOfBoundsAccess = true; 1583 1584 // Check if the shader operation accessed an operand located less than 16 bytes away 1585 // from the out of bounds address. 1586 if (!isOutOfBoundsAccess && distanceToOutOfBounds < 16) 1587 { 1588 deUint32 operandSize = 0; 1589 1590 switch (m_shaderType) 1591 { 1592 case SHADER_TYPE_SCALAR_COPY: 1593 operandSize = 4; // Size of scalar 1594 break; 1595 1596 case SHADER_TYPE_VECTOR_COPY: 1597 operandSize = 4 * 4; // Size of vec4 1598 break; 1599 1600 case SHADER_TYPE_MATRIX_COPY: 1601 operandSize = 4 * 16; // Size of mat4 1602 break; 1603 1604 default: 1605 DE_ASSERT(false); 1606 } 1607 1608 isOutOfBoundsAccess = (((offsetInBytes / operandSize) + 1) * operandSize > maxAccessRange); 1609 } 1610 1611 if (isOutOfBoundsAccess) 1612 { 1613 logMsg << " (out of bounds " << (isReadAccess ? "read": "write") << ")"; 1614 1615 const bool isValuePartiallyOutOfBounds = ((distanceToOutOfBounds > 0) && ((deUint32)distanceToOutOfBounds < 4)); 1616 bool isValidValue = false; 1617 1618 if (isValuePartiallyOutOfBounds && !m_accessOutOfBackingMemory) 1619 { 1620 // The value is partially out of bounds 1621 1622 bool isOutOfBoundsPartOk = true; 1623 bool isWithinBoundsPartOk = true; 1624 1625 if (isReadAccess) 1626 { 1627 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, distanceToOutOfBounds); 1628 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, (deUint8*)outValuePtr + distanceToOutOfBounds , outValueSize - distanceToOutOfBounds); 1629 } 1630 else 1631 { 1632 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, distanceToOutOfBounds) 1633 || isOutBufferValueUnchanged(offsetInBytes, distanceToOutOfBounds); 1634 1635 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, (deUint8*)outValuePtr + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds) 1636 || isOutBufferValueUnchanged(offsetInBytes + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds); 1637 } 1638 1639 logMsg << ", first " << distanceToOutOfBounds << " byte(s) " << (isWithinBoundsPartOk ? "OK": "wrong"); 1640 logMsg << ", last " << outValueSize - distanceToOutOfBounds << " byte(s) " << (isOutOfBoundsPartOk ? "OK": "wrong"); 1641 1642 isValidValue = isWithinBoundsPartOk && isOutOfBoundsPartOk; 1643 } 1644 else 1645 { 1646 if (isReadAccess) 1647 { 1648 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, outValueSize); 1649 } 1650 else 1651 { 1652 isValidValue = isOutBufferValueUnchanged(offsetInBytes, outValueSize); 1653 1654 if (!isValidValue) 1655 { 1656 // Out of bounds writes may modify values withing the memory ranges bound to the buffer 1657 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, outValueSize); 1658 1659 if (isValidValue) 1660 logMsg << ", OK, written within the memory range bound to the buffer"; 1661 } 1662 } 1663 } 1664 1665 if (!isValidValue) 1666 { 1667 // Check if we are satisfying the [0, 0, 0, x] pattern, where x may be either 0 or 1, 1668 // or the maximum representable positive integer value (if the format is integer-based). 1669 1670 const bool canMatchVec4Pattern = (isReadAccess 1671 && !isValuePartiallyOutOfBounds 1672 && (m_shaderType == SHADER_TYPE_VECTOR_COPY) 1673 && (offsetInBytes / 4 + 1) % 4 == 0); 1674 bool matchesVec4Pattern = false; 1675 1676 if (canMatchVec4Pattern) 1677 { 1678 matchesVec4Pattern = verifyOutOfBoundsVec4(static_cast<const deUint32*>(static_cast<const void*>(outValuePtr)) - 3, m_bufferFormat); 1679 } 1680 1681 if (!canMatchVec4Pattern || !matchesVec4Pattern) 1682 { 1683 logMsg << ". Failed: "; 1684 1685 if (isReadAccess) 1686 { 1687 logMsg << "expected value within the buffer range or 0"; 1688 1689 if (canMatchVec4Pattern) 1690 logMsg << ", or the [0, 0, 0, x] pattern"; 1691 } 1692 else 1693 { 1694 logMsg << "written out of the range"; 1695 } 1696 1697 allOk = false; 1698 } 1699 } 1700 } 1701 else // We are within bounds 1702 { 1703 if (isReadAccess) 1704 { 1705 if (!isExpectedValueFromInBuffer(offsetInBytes, outValuePtr, 4)) 1706 { 1707 logMsg << ", Failed: unexpected value"; 1708 allOk = false; 1709 } 1710 } 1711 else 1712 { 1713 // Out of bounds writes may change values within the bounds. 1714 if (!isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.accessRange, outValuePtr, 4)) 1715 { 1716 logMsg << ", Failed: unexpected value"; 1717 allOk = false; 1718 } 1719 } 1720 } 1721 } 1722 } 1723 1724 log << tcu::TestLog::Message << logMsg.str() << tcu::TestLog::EndMessage; 1725 1726 return allOk; 1727 } 1728 1729 // BufferReadInstance 1730 1731 ReadInstance::ReadInstance (Context& context, 1732 Move<VkDevice> device, 1733 ShaderType shaderType, 1734 VkShaderStageFlags shaderStage, 1735 VkFormat bufferFormat, 1736 //bool readFromStorage, 1737 VkDeviceSize inBufferAccessRange, 1738 bool accessOutOfBackingMemory) 1739 1740 : AccessInstance (context, device, shaderType, shaderStage, bufferFormat, 1741 BUFFER_ACCESS_TYPE_READ_FROM_STORAGE, 1742 inBufferAccessRange, RobustAccessWithPointersTest::s_numberOfBytesAccessed, 1743 accessOutOfBackingMemory) 1744 { 1745 } 1746 1747 // BufferWriteInstance 1748 1749 WriteInstance::WriteInstance (Context& context, 1750 Move<VkDevice> device, 1751 ShaderType shaderType, 1752 VkShaderStageFlags shaderStage, 1753 VkFormat bufferFormat, 1754 VkDeviceSize writeBufferAccessRange, 1755 bool accessOutOfBackingMemory) 1756 1757 : AccessInstance (context, device, shaderType, shaderStage, bufferFormat, 1758 BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE, 1759 RobustAccessWithPointersTest::s_numberOfBytesAccessed, writeBufferAccessRange, 1760 accessOutOfBackingMemory) 1761 { 1762 } 1763 1764 } // unnamed namespace 1765 1766 tcu::TestCaseGroup* createBufferAccessWithVariablePointersTests(tcu::TestContext& testCtx) 1767 { 1768 // Lets make group for the tests 1769 de::MovePtr<tcu::TestCaseGroup> bufferAccessWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "through_pointers", "")); 1770 1771 // Lets add subgroups to better organise tests 1772 de::MovePtr<tcu::TestCaseGroup> computeWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "compute", "")); 1773 de::MovePtr<tcu::TestCaseGroup> computeReads (new tcu::TestCaseGroup(testCtx, "reads", "")); 1774 de::MovePtr<tcu::TestCaseGroup> computeWrites (new tcu::TestCaseGroup(testCtx, "writes", "")); 1775 1776 de::MovePtr<tcu::TestCaseGroup> graphicsWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "graphics", "")); 1777 de::MovePtr<tcu::TestCaseGroup> graphicsReads (new tcu::TestCaseGroup(testCtx, "reads", "")); 1778 de::MovePtr<tcu::TestCaseGroup> graphicsReadsVertex (new tcu::TestCaseGroup(testCtx, "vertex", "")); 1779 de::MovePtr<tcu::TestCaseGroup> graphicsReadsFragment (new tcu::TestCaseGroup(testCtx, "fragment", "")); 1780 de::MovePtr<tcu::TestCaseGroup> graphicsWrites (new tcu::TestCaseGroup(testCtx, "writes", "")); 1781 de::MovePtr<tcu::TestCaseGroup> graphicsWritesVertex (new tcu::TestCaseGroup(testCtx, "vertex", "")); 1782 de::MovePtr<tcu::TestCaseGroup> graphicsWritesFragment (new tcu::TestCaseGroup(testCtx, "fragment", "")); 1783 1784 // A struct for describing formats 1785 struct Formats 1786 { 1787 const VkFormat value; 1788 const char * const name; 1789 }; 1790 1791 const Formats bufferFormats[] = 1792 { 1793 { VK_FORMAT_R32_SINT, "s32" }, 1794 { VK_FORMAT_R32_UINT, "u32" }, 1795 { VK_FORMAT_R32_SFLOAT, "f32" } 1796 }; 1797 const deUint8 bufferFormatsCount = static_cast<deUint8>(DE_LENGTH_OF_ARRAY(bufferFormats)); 1798 1799 // Amounts of data to copy 1800 const VkDeviceSize rangeSizes[] = 1801 { 1802 1ull, 3ull, 4ull, 16ull, 32ull 1803 }; 1804 const deUint8 rangeSizesCount = static_cast<deUint8>(DE_LENGTH_OF_ARRAY(rangeSizes)); 1805 1806 // gather above data into one array 1807 const struct ShaderTypes 1808 { 1809 const ShaderType value; 1810 const char * const name; 1811 const Formats* const formats; 1812 const deUint8 formatsCount; 1813 const VkDeviceSize* const sizes; 1814 const deUint8 sizesCount; 1815 } types[] = 1816 { 1817 { SHADER_TYPE_VECTOR_COPY, "vec4", bufferFormats, bufferFormatsCount, rangeSizes, rangeSizesCount }, 1818 { SHADER_TYPE_SCALAR_COPY, "scalar", bufferFormats, bufferFormatsCount, rangeSizes, rangeSizesCount } 1819 }; 1820 1821 // Specify to which subgroups put various tests 1822 const struct ShaderStages 1823 { 1824 VkShaderStageFlags stage; 1825 de::MovePtr<tcu::TestCaseGroup>& reads; 1826 de::MovePtr<tcu::TestCaseGroup>& writes; 1827 } stages[] = 1828 { 1829 { VK_SHADER_STAGE_VERTEX_BIT, graphicsReadsVertex, graphicsWritesVertex }, 1830 { VK_SHADER_STAGE_FRAGMENT_BIT, graphicsReadsFragment, graphicsWritesFragment }, 1831 { VK_SHADER_STAGE_COMPUTE_BIT, computeReads, computeWrites } 1832 }; 1833 1834 // Eventually specify if memory used should be in the "inaccesible" portion of buffer or entirely outside of buffer 1835 const char* const backingMemory[] = { "in_memory", "out_of_memory" }; 1836 1837 for (deInt32 stageId = 0; stageId < DE_LENGTH_OF_ARRAY(stages); ++stageId) 1838 for (int i = 0; i < DE_LENGTH_OF_ARRAY(types); ++i) 1839 for (int j = 0; j < types[i].formatsCount; ++j) 1840 for (int k = 0; k < types[i].sizesCount; ++k) 1841 for (int s = 0; s < DE_LENGTH_OF_ARRAY(backingMemory); ++s) 1842 { 1843 std::ostringstream name; 1844 name << types[i].sizes[k] << "B_" << backingMemory[s] << "_with_" << types[i].name << '_' << types[i].formats[j].name; 1845 stages[stageId].reads->addChild(new RobustReadTest(testCtx, name.str().c_str(), "", stages[stageId].stage, types[i].value, types[i].formats[j].value, types[i].sizes[k], s != 0)); 1846 } 1847 1848 for (deInt32 stageId = 0; stageId < DE_LENGTH_OF_ARRAY(stages); ++stageId) 1849 for (int i=0; i<DE_LENGTH_OF_ARRAY(types); ++i) 1850 for (int j=0; j<types[i].formatsCount; ++j) 1851 for (int k = 0; k<types[i].sizesCount; ++k) 1852 for (int s = 0; s < DE_LENGTH_OF_ARRAY(backingMemory); ++s) 1853 { 1854 std::ostringstream name; 1855 name << types[i].sizes[k] << "B_" << backingMemory[s] << "_with_" << types[i].name << '_' << types[i].formats[j].name; 1856 stages[stageId].writes->addChild(new RobustWriteTest(testCtx, name.str().c_str(), "", stages[stageId].stage, types[i].value, types[i].formats[j].value, types[i].sizes[k], s != 0)); 1857 } 1858 1859 graphicsReads->addChild(graphicsReadsVertex.release()); 1860 graphicsReads->addChild(graphicsReadsFragment.release()); 1861 1862 graphicsWrites->addChild(graphicsWritesVertex.release()); 1863 graphicsWrites->addChild(graphicsWritesFragment.release()); 1864 1865 graphicsWithVariablePointersTests->addChild(graphicsReads.release()); 1866 graphicsWithVariablePointersTests->addChild(graphicsWrites.release()); 1867 1868 computeWithVariablePointersTests->addChild(computeReads.release()); 1869 computeWithVariablePointersTests->addChild(computeWrites.release()); 1870 1871 bufferAccessWithVariablePointersTests->addChild(graphicsWithVariablePointersTests.release()); 1872 bufferAccessWithVariablePointersTests->addChild(computeWithVariablePointersTests.release()); 1873 1874 return bufferAccessWithVariablePointersTests.release(); 1875 } 1876 1877 } // robustness 1878 } // vkt 1879