1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2018 The Khronos Group Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Robust buffer access tests for storage buffers and
22 * storage texel buffers with variable pointers.
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.
36 *//*--------------------------------------------------------------------*/
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 "vkDeviceUtil.hpp"
47 #include "vkRefUtil.hpp"
48 #include "vkTypeUtil.hpp"
49 #include "tcuTestLog.hpp"
51 #include "deRandom.hpp"
63 // keep local things local
67 // Creates a custom device with robust buffer access and variable pointer features.
68 Move<VkDevice> createRobustBufferAccessVariablePointersDevice (Context& context)
70 auto pointerFeatures = context.getVariablePointersFeatures();
72 VkPhysicalDeviceFeatures2 features2 = initVulkanStructure();
73 features2.features = context.getDeviceFeatures();
74 features2.features.robustBufferAccess = VK_TRUE;
75 features2.pNext = &pointerFeatures;
77 return createRobustBufferAccessDevice(context, &features2);
80 // A supplementary structures that can hold information about buffer size
81 struct AccessRangesData
83 VkDeviceSize allocSize;
84 VkDeviceSize accessRange;
85 VkDeviceSize maxAccessRange;
88 // 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
89 typedef void(*FillBufferProcPtr)(void*, vk::VkDeviceSize, const void* const);
91 // An utility function for creating a buffer
92 // This function not only allocates memory for the buffer but also fills buffer up with a data
93 void createTestBuffer (Context& context,
94 const vk::DeviceInterface& deviceInterface,
95 const VkDevice& device,
96 VkDeviceSize accessRange,
97 VkBufferUsageFlags usage,
98 SimpleAllocator& allocator,
99 Move<VkBuffer>& buffer,
100 de::MovePtr<Allocation>& bufferAlloc,
101 AccessRangesData& data,
102 FillBufferProcPtr fillBufferProc,
103 const void* const blob)
105 const VkBufferCreateInfo bufferParams =
107 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
108 DE_NULL, // const void* pNext;
109 0u, // VkBufferCreateFlags flags;
110 accessRange, // VkDeviceSize size;
111 usage, // VkBufferUsageFlags usage;
112 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
113 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount;
114 DE_NULL // const deUint32* pQueueFamilyIndices;
117 buffer = createBuffer(deviceInterface, device, &bufferParams);
119 VkMemoryRequirements bufferMemoryReqs = getBufferMemoryRequirements(deviceInterface, device, *buffer);
120 bufferAlloc = allocator.allocate(bufferMemoryReqs, MemoryRequirement::HostVisible);
122 data.allocSize = bufferMemoryReqs.size;
123 data.accessRange = accessRange;
124 data.maxAccessRange = deMinu64(data.allocSize, deMinu64(bufferParams.size, accessRange));
126 VK_CHECK(deviceInterface.bindBufferMemory(device, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset()));
127 #ifdef CTS_USES_VULKANSC
128 if(context.getTestContext().getCommandLine().isSubProcess())
129 fillBufferProc(bufferAlloc->getHostPtr(), bufferMemoryReqs.size, blob);
131 fillBufferProc(bufferAlloc->getHostPtr(), bufferMemoryReqs.size, blob);
133 #endif // CTS_USES_VULKANCSC
134 flushMappedMemoryRange(deviceInterface, device, bufferAlloc->getMemory(), bufferAlloc->getOffset(), VK_WHOLE_SIZE);
137 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with "randomly" generated test data matching desired format.
138 void populateBufferWithValues (void* buffer,
140 const void* const blob)
142 populateBufferWithTestValues(buffer, size, *static_cast<const vk::VkFormat*>(blob));
145 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with 0xBABABABABABA... pattern. Used to fill up output buffers.
146 // Since this pattern cannot show up in generated test data it should not show up in the valid output.
147 void populateBufferWithFiller (void* buffer,
149 const void* const blob)
152 deMemset(buffer, 0xBA, static_cast<size_t>(size));
155 // An adapter function matching FillBufferProcPtr interface. Fills a buffer with a copy of memory contents pointed to by blob.
156 void populateBufferWithCopy (void* buffer,
158 const void* const blob)
160 deMemcpy(buffer, blob, static_cast<size_t>(size));
163 // A composite types used in test
164 // Those composites can be made of unsigned ints, signed ints or floats (except for matrices that work with floats only).
167 SHADER_TYPE_MATRIX_COPY = 0,
168 SHADER_TYPE_VECTOR_COPY,
169 SHADER_TYPE_SCALAR_COPY,
174 // We are testing reads or writes
175 // In case of testing reads - writes are always
176 enum BufferAccessType
178 BUFFER_ACCESS_TYPE_READ_FROM_STORAGE = 0,
179 BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE,
182 // Test case for checking robust buffer access with variable pointers
183 class RobustAccessWithPointersTest : public vkt::TestCase
186 static const deUint32 s_testArraySize;
187 static const deUint32 s_numberOfBytesAccessed;
189 RobustAccessWithPointersTest (tcu::TestContext& testContext,
190 const std::string& name,
191 const std::string& description,
192 VkShaderStageFlags shaderStage,
193 ShaderType shaderType,
194 VkFormat bufferFormat);
196 virtual ~RobustAccessWithPointersTest (void)
200 void checkSupport (Context &context) const override;
203 const VkShaderStageFlags m_shaderStage;
204 const ShaderType m_shaderType;
205 const VkFormat m_bufferFormat;
208 const deUint32 RobustAccessWithPointersTest::s_testArraySize = 1024u;
209 const deUint32 RobustAccessWithPointersTest::s_numberOfBytesAccessed = static_cast<deUint32>(16ull * sizeof(float));
211 RobustAccessWithPointersTest::RobustAccessWithPointersTest(tcu::TestContext& testContext,
212 const std::string& name,
213 const std::string& description,
214 VkShaderStageFlags shaderStage,
215 ShaderType shaderType,
216 VkFormat bufferFormat)
217 : vkt::TestCase(testContext, name, description)
218 , m_shaderStage(shaderStage)
219 , m_shaderType(shaderType)
220 , m_bufferFormat(bufferFormat)
222 DE_ASSERT(m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT || m_shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT || m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT);
225 void RobustAccessWithPointersTest::checkSupport (Context &context) const
227 const auto& pointerFeatures = context.getVariablePointersFeatures();
228 if (!pointerFeatures.variablePointersStorageBuffer)
229 TCU_THROW(NotSupportedError, "VariablePointersStorageBuffer SPIR-V capability not supported");
231 if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") && !context.getDeviceFeatures().robustBufferAccess)
232 TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: robustBufferAccess not supported by this implementation");
235 // A subclass for testing reading with variable pointers
236 class RobustReadTest : public RobustAccessWithPointersTest
239 RobustReadTest (tcu::TestContext& testContext,
240 const std::string& name,
241 const std::string& description,
242 VkShaderStageFlags shaderStage,
243 ShaderType shaderType,
244 VkFormat bufferFormat,
245 VkDeviceSize readAccessRange,
246 bool accessOutOfBackingMemory);
248 virtual ~RobustReadTest (void)
250 virtual TestInstance* createInstance (Context& context) const;
252 virtual void initPrograms (SourceCollections& programCollection) const;
253 const VkDeviceSize m_readAccessRange;
254 const bool m_accessOutOfBackingMemory;
257 // A subclass for testing writing with variable pointers
258 class RobustWriteTest : public RobustAccessWithPointersTest
261 RobustWriteTest (tcu::TestContext& testContext,
262 const std::string& name,
263 const std::string& description,
264 VkShaderStageFlags shaderStage,
265 ShaderType shaderType,
266 VkFormat bufferFormat,
267 VkDeviceSize writeAccessRange,
268 bool accessOutOfBackingMemory);
270 virtual ~RobustWriteTest (void) {}
271 virtual TestInstance* createInstance (Context& context) const;
273 virtual void initPrograms (SourceCollections& programCollection) const;
274 const VkDeviceSize m_writeAccessRange;
275 const bool m_accessOutOfBackingMemory;
278 // In case I detect that some prerequisites are not fullfilled I am creating this lightweight empty test instance instead of AccessInstance. Should be bit faster that way.
279 class NotSupportedInstance : public vkt::TestInstance
282 NotSupportedInstance (Context& context,
283 const std::string& message)
284 : TestInstance(context)
285 , m_notSupportedMessage(message)
288 virtual ~NotSupportedInstance (void)
292 virtual tcu::TestStatus iterate (void)
294 TCU_THROW(NotSupportedError, m_notSupportedMessage.c_str());
298 std::string m_notSupportedMessage;
301 // A superclass for instances testing reading and writing
302 // holds all necessary object members
303 class AccessInstance : public vkt::TestInstance
306 AccessInstance (Context& context,
307 Move<VkDevice> device,
308 #ifndef CTS_USES_VULKANSC
309 de::MovePtr<vk::DeviceDriver> deviceDriver,
311 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
312 #endif // CTS_USES_VULKANSC
313 ShaderType shaderType,
314 VkShaderStageFlags shaderStage,
315 VkFormat bufferFormat,
316 BufferAccessType bufferAccessType,
317 VkDeviceSize inBufferAccessRange,
318 VkDeviceSize outBufferAccessRange,
319 bool accessOutOfBackingMemory);
321 virtual ~AccessInstance (void);
323 virtual tcu::TestStatus iterate (void);
325 virtual bool verifyResult (bool splitAccess = false);
328 bool isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes,
329 const void* valuePtr,
330 VkDeviceSize valueSize);
331 bool isOutBufferValueUnchanged (VkDeviceSize offsetInBytes,
332 VkDeviceSize valueSize);
335 Move<VkDevice> m_device;
336 #ifndef CTS_USES_VULKANSC
337 de::MovePtr<vk::DeviceDriver> m_deviceDriver;
339 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> m_deviceDriver;
340 #endif // CTS_USES_VULKANSC
341 de::MovePtr<TestEnvironment>m_testEnvironment;
343 const ShaderType m_shaderType;
344 const VkShaderStageFlags m_shaderStage;
346 const VkFormat m_bufferFormat;
347 const BufferAccessType m_bufferAccessType;
349 AccessRangesData m_inBufferAccess;
350 Move<VkBuffer> m_inBuffer;
351 de::MovePtr<Allocation> m_inBufferAlloc;
353 AccessRangesData m_outBufferAccess;
354 Move<VkBuffer> m_outBuffer;
355 de::MovePtr<Allocation> m_outBufferAlloc;
357 Move<VkBuffer> m_indicesBuffer;
358 de::MovePtr<Allocation> m_indicesBufferAlloc;
360 Move<VkDescriptorPool> m_descriptorPool;
361 Move<VkDescriptorSetLayout> m_descriptorSetLayout;
362 Move<VkDescriptorSet> m_descriptorSet;
364 Move<VkFence> m_fence;
367 // Used when m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT
368 Move<VkBuffer> m_vertexBuffer;
369 de::MovePtr<Allocation> m_vertexBufferAlloc;
371 const bool m_accessOutOfBackingMemory;
374 // A subclass for read tests
375 class ReadInstance: public AccessInstance
378 ReadInstance (Context& context,
379 Move<VkDevice> device,
380 #ifndef CTS_USES_VULKANSC
381 de::MovePtr<vk::DeviceDriver> deviceDriver,
383 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
384 #endif // CTS_USES_VULKANSC
385 ShaderType shaderType,
386 VkShaderStageFlags shaderStage,
387 VkFormat bufferFormat,
388 VkDeviceSize inBufferAccessRange,
389 bool accessOutOfBackingMemory);
391 virtual ~ReadInstance (void) {}
394 // A subclass for write tests
395 class WriteInstance: public AccessInstance
398 WriteInstance (Context& context,
399 Move<VkDevice> device,
400 #ifndef CTS_USES_VULKANSC
401 de::MovePtr<vk::DeviceDriver> deviceDriver,
403 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
404 #endif // CTS_USES_VULKANSC
405 ShaderType shaderType,
406 VkShaderStageFlags shaderStage,
407 VkFormat bufferFormat,
408 VkDeviceSize writeBufferAccessRange,
409 bool accessOutOfBackingMemory);
411 virtual ~WriteInstance (void) {}
414 // Automatically incremented counter.
415 // Each read of value bumps counter up.
422 deUint32 incrementAndGetValue()
430 // A class representing SPIRV variable.
431 // This class internally has an unique identificator.
432 // When such variable is used in shader composition routine it is mapped on a in-SPIRV-code variable name.
435 friend bool operator < (const Variable& a, const Variable& b);
437 Variable(Autocounter& autoincrement)
438 : value(autoincrement.incrementAndGetValue())
444 bool operator < (const Variable& a, const Variable& b)
446 return a.value < b.value;
449 // A class representing SPIRV operation.
450 // Since those are not copyable they don't need internal id. Memory address is used instead.
453 friend bool operator==(const Operation& a, const Operation& b);
455 Operation(const char* text)
459 const std::string& getValue() const
465 Operation(const Operation& other);
466 const std::string value;
469 bool operator == (const Operation& a, const Operation& b)
471 return &a == &b; // a fast & simple address comparison - making copies was disabled
474 // A namespace containing all SPIRV operations used in those tests.
476 #define OP(name) const Operation name("Op"#name)
500 OP(TypeSampledImage);
503 OP(ConstantComposite);
521 // A class that allows to easily compose SPIRV code.
522 // This class automatically keeps correct order of most of operations
523 // i.e. capabilities to the top,
529 // composes shader string out of shader substreams.
530 std::string str () const
532 std::stringstream stream;
533 stream << capabilities.str()
534 << "; ----------------- PREAMBLE -----------------\n"
536 << "; ----------------- DEBUG --------------------\n"
538 << "; ----------------- DECORATIONS --------------\n"
540 << "; ----------------- TYPES --------------------\n"
542 << "; ----------------- CONSTANTS ----------------\n"
544 << "; ----------------- ADVANCED TYPES -----------\n"
545 << compositetypes.str()
546 << ((compositeconstants.str().length() > 0) ? "; ----------------- CONSTANTS ----------------\n" : "")
547 << compositeconstants.str()
548 << "; ----------------- VARIABLES & FUNCTIONS ----\n"
549 << shaderstream.str();
552 // Functions below are used to push Operations, Variables and other strings, numbers and characters to the shader.
553 // Each function uses selectStream and map subroutines.
554 // selectStream is used to choose a proper substream of shader.
555 // E.g. if an operation is OpConstant it should be put into constants definitions stream - so selectStream will return that stream.
556 // map on the other hand is used to replace Variables and Operations to their in-SPIRV-code representations.
557 // for types like ints or floats map simply calls << operator to produce its string representation
558 // for Operations a proper operation string is returned
559 // for Variables there is a special mapping between in-C++ variable and in-SPIRV-code variable name.
560 // following sequence of functions could be squashed to just two using variadic templates once we move to C++11 or higher
561 // each method returns *this to allow chaining calls to these methods.
562 template <typename T>
563 ShaderStream& operator () (const T& a)
565 selectStream(a, 0) << map(a) << '\n';
568 template <typename T1, typename T2>
569 ShaderStream& operator () (const T1& a, const T2& b)
571 selectStream(a, 0) << map(a) << '\t' << map(b) << '\n';
574 template <typename T1, typename T2, typename T3>
575 ShaderStream& operator () (const T1& a, const T2& b, const T3& c)
577 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\n';
580 template <typename T1, typename T2, typename T3, typename T4>
581 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d)
583 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\n';
586 template <typename T1, typename T2, typename T3, typename T4, typename T5>
587 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e)
589 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\n';
592 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
593 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f)
595 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\n';
598 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
599 ShaderStream& operator () (const T1& a, const T2& b, const T3& c, const T4& d, const T5& e, const T6& f, const T7& g)
601 selectStream(a, c) << map(a) << '\t' << map(b) << '\t' << map(c) << '\t' << map(d) << '\t' << map(e) << '\t' << map(f) << '\t' << map(g) << '\n';
604 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
605 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)
607 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';
610 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
611 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)
613 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';
616 template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10>
617 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)
619 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';
623 // returns true if two variables has the same in-SPIRV-code names
624 bool areSame (const Variable a, const Variable b)
626 VariableIt varA = vars.find(a);
627 VariableIt varB = vars.find(b);
628 return varA != vars.end() && varB != vars.end() && varA->second == varB->second;
631 // makes variable 'a' in-SPIRV-code name to be the same as variable 'b' in-SPIRV-code name
632 void makeSame (const Variable a, const Variable b)
634 VariableIt varB = vars.find(b);
635 if (varB != vars.end())
637 std::pair<VariableIt, bool> inserted = vars.insert(std::make_pair(a, varB->second));
638 if (!inserted.second)
639 inserted.first->second = varB->second;
643 // generic version of map (tries to push whatever came to stringstream to get its string representation)
644 template <typename T>
645 std::string map (const T& a)
647 std::stringstream temp;
652 // looks for mapping of c++ Variable object onto in-SPIRV-code name.
653 // if there was not yet such mapping generated a new mapping is created based on incremented local counter.
654 std::string map (const Variable& a)
656 VariableIt var = vars.find(a);
657 if (var != vars.end())
659 std::stringstream temp;
663 temp << std::hex << varCounter.incrementAndGetValue();
664 vars.insert(std::make_pair(a, temp.str()));
668 // a simple specification for Operation
669 std::string map (const Operation& a)
674 // a specification for char* - faster than going through stringstream << operator
675 std::string map (const char*& a)
677 return std::string(a);
680 // a specification for char - faster than going through stringstream << operator
681 std::string map (const char& a)
683 return std::string(1, a);
686 // a generic version of selectStream - used when neither 1st nor 3rd SPIRV line token is Operation.
687 // In general should never happen.
688 // All SPIRV lines are constructed in a one of two forms:
689 // Variable = Operation operands...
691 // Operation operands...
692 // So operation is either 1st or 3rd token.
693 template <typename T0, typename T1>
694 std::stringstream& selectStream (const T0& op0, const T1& op1)
701 // Specialisation for Operation being 1st parameter
702 // Certain operations make the SPIRV code line to be pushed to different substreams.
703 template <typename T1>
704 std::stringstream& selectStream (const Operation& op, const T1& op1)
707 if (op == op::Decorate || op == op::MemberDecorate)
709 if (op == op::Name || op == op::MemberName)
711 if (op == op::Capability || op == op::Extension)
713 if (op == op::MemoryModel || op == op::ExecutionMode || op == op::EntryPoint)
718 // Specialisation for Operation being 3rd parameter
719 // Certain operations make the SPIRV code line to be pushed to different substreams.
720 // If we would like to use this way of generating SPIRV we could use this method as SPIRV line validation point
721 // e.g. here instead of heving partial specialisation I could specialise for T0 being Variable since this has to match Variable = Operation operands...
722 template <typename T0>
723 std::stringstream& selectStream (const T0& op0, const Operation& op)
726 if (op == op::ExtInstImport)
728 if (op == op::TypeVoid || op == op::TypeBool || op == op::TypeInt || op == op::TypeFloat || op == op::TypeVector || op == op::TypeMatrix)
730 if (op == op::TypeArray || op == op::TypeStruct || op == op::TypeFunction || op == op::TypePointer || op == op::TypeImage || op == op::TypeSampledImage)
731 return compositetypes;
732 if (op == op::Constant)
734 if (op == op::ConstantComposite)
735 return compositeconstants;
739 typedef std::map<Variable, std::string> VariablesPack;
740 typedef VariablesPack::iterator VariableIt;
742 // local mappings between c++ Variable objects and in-SPIRV-code names
746 std::stringstream capabilities;
747 std::stringstream preamble;
748 std::stringstream names;
749 std::stringstream decorations;
750 std::stringstream basictypes;
751 std::stringstream constants;
752 std::stringstream compositetypes;
753 std::stringstream compositeconstants;
754 std::stringstream shaderstream;
756 // local incremented counter
757 Autocounter varCounter;
760 // A suppliementary class to group frequently used Variables together
764 Variables (Autocounter &autoincrement)
765 : version(autoincrement)
766 , mainFunc(autoincrement)
767 , mainFuncLabel(autoincrement)
768 , voidFuncVoid(autoincrement)
769 , copy_type(autoincrement)
770 , copy_type_vec(autoincrement)
771 , buffer_type_vec(autoincrement)
772 , copy_type_ptr(autoincrement)
773 , buffer_type(autoincrement)
774 , voidId(autoincrement)
775 , v4f32(autoincrement)
776 , v4s32(autoincrement)
777 , v4u32(autoincrement)
778 , v4s64(autoincrement)
779 , v4u64(autoincrement)
785 , boolean(autoincrement)
786 , array_content_type(autoincrement)
787 , s32_type_ptr(autoincrement)
788 , dataSelectorStructPtrType(autoincrement)
789 , dataSelectorStructPtr(autoincrement)
790 , dataArrayType(autoincrement)
791 , dataInput(autoincrement)
792 , dataInputPtrType(autoincrement)
793 , dataInputType(autoincrement)
794 , dataInputSampledType(autoincrement)
795 , dataOutput(autoincrement)
796 , dataOutputPtrType(autoincrement)
797 , dataOutputType(autoincrement)
798 , dataSelectorStructType(autoincrement)
799 , input(autoincrement)
800 , inputPtr(autoincrement)
801 , output(autoincrement)
802 , outputPtr(autoincrement)
804 for (deUint32 i = 0; i < 32; ++i)
805 constants.push_back(Variable(autoincrement));
807 const Variable version;
808 const Variable mainFunc;
809 const Variable mainFuncLabel;
810 const Variable voidFuncVoid;
811 std::vector<Variable> constants;
812 const Variable copy_type;
813 const Variable copy_type_vec;
814 const Variable buffer_type_vec;
815 const Variable copy_type_ptr;
816 const Variable buffer_type;
817 const Variable voidId;
818 const Variable v4f32;
819 const Variable v4s32;
820 const Variable v4u32;
821 const Variable v4s64;
822 const Variable v4u64;
828 const Variable boolean;
829 const Variable array_content_type;
830 const Variable s32_type_ptr;
831 const Variable dataSelectorStructPtrType;
832 const Variable dataSelectorStructPtr;
833 const Variable dataArrayType;
834 const Variable dataInput;
835 const Variable dataInputPtrType;
836 const Variable dataInputType;
837 const Variable dataInputSampledType;
838 const Variable dataOutput;
839 const Variable dataOutputPtrType;
840 const Variable dataOutputType;
841 const Variable dataSelectorStructType;
842 const Variable input;
843 const Variable inputPtr;
844 const Variable output;
845 const Variable outputPtr;
848 // A routing generating SPIRV code for all test cases in this group
849 std::string MakeShader(VkShaderStageFlags shaderStage, ShaderType shaderType, VkFormat bufferFormat, bool reads, bool unused)
851 const bool isR64 = (bufferFormat == VK_FORMAT_R64_UINT || bufferFormat == VK_FORMAT_R64_SINT);
855 // variables require such counter to generate their unique ids. Since there is possibility that in the future this code will
856 // run parallel this counter is made local to this function body to be safe.
857 Autocounter localcounter;
859 // A frequently used Variables (gathered into this single object for readability)
860 Variables var (localcounter);
862 // A SPIRV code builder
863 ShaderStream shaderSource;
865 // A basic preamble of SPIRV shader. Turns on required capabilities and extensions.
867 (op::Capability, "Shader")
868 (op::Capability, "VariablePointersStorageBuffer");
873 (op::Capability, "Int64");
877 (op::Extension, "\"SPV_KHR_storage_buffer_storage_class\"")
878 (op::Extension, "\"SPV_KHR_variable_pointers\"")
879 (var.version, is, op::ExtInstImport, "\"GLSL.std.450\"")
880 (op::MemoryModel, "Logical", "GLSL450");
882 // Use correct entry point definition depending on shader stage
883 if (shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
886 (op::EntryPoint, "GLCompute", var.mainFunc, "\"main\"")
887 (op::ExecutionMode, var.mainFunc, "LocalSize", 1, 1, 1);
889 else if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
892 (op::EntryPoint, "Vertex", var.mainFunc, "\"main\"", var.input, var.output)
893 (op::Decorate, var.output, "BuiltIn", "Position")
894 (op::Decorate, var.input, "Location", 0);
896 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
899 (op::EntryPoint, "Fragment", var.mainFunc, "\"main\"", var.output)
900 (op::ExecutionMode, var.mainFunc, "OriginUpperLeft")
901 (op::Decorate, var.output, "Location", 0);
904 // If we are testing vertex shader or fragment shader we need to provide the other one for the pipeline too.
905 // So the not tested one is 'unused'. It is then a minimal/simplest possible pass-through shader.
906 // If we are testing compute shader we dont need unused shader at all.
909 if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
912 (var.voidId, is, op::TypeVoid)
913 (var.voidFuncVoid, is, op::TypeFunction, var.voidId)
914 (var.f32, is, op::TypeFloat, 32)
915 (var.v4f32, is, op::TypeVector, var.f32, 4)
916 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32)
917 (var.output, is, op::Variable, var.outputPtr, "Output")
918 (var.constants[6], is, op::Constant, var.f32, 1)
919 (var.constants[7], is, op::ConstantComposite, var.v4f32, var.constants[6], var.constants[6], var.constants[6], var.constants[6])
920 (var.mainFunc, is, op::Function, var.voidId, "None", var.voidFuncVoid)
921 (var.mainFuncLabel, is, op::Label);
923 else if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
926 (var.voidId, is, op::TypeVoid)
927 (var.voidFuncVoid, is, op::TypeFunction , var.voidId)
928 (var.f32, is, op::TypeFloat, 32)
929 (var.v4f32, is, op::TypeVector , var.f32, 4)
930 (var.outputPtr, is, op::TypePointer, "Output" , var.v4f32)
931 (var.output, is, op::Variable , var.outputPtr, "Output")
932 (var.inputPtr, is, op::TypePointer, "Input" , var.v4f32)
933 (var.input, is, op::Variable , var.inputPtr, "Input")
934 (var.mainFunc, is, op::Function , var.voidId, "None", var.voidFuncVoid)
935 (var.mainFuncLabel, is, op::Label);
938 else // this is a start of actual shader that tests variable pointers
941 (op::Decorate, var.dataInput, "DescriptorSet", 0)
942 (op::Decorate, var.dataInput, "Binding", 0)
944 (op::Decorate, var.dataOutput, "DescriptorSet", 0)
945 (op::Decorate, var.dataOutput, "Binding", 1);
947 // for scalar types and vector types we use 1024 element array of 4 elements arrays of 4-component vectors
948 // so the stride of internal array is size of 4-component vector
949 if (shaderType == SHADER_TYPE_SCALAR_COPY || shaderType == SHADER_TYPE_VECTOR_COPY)
954 (op::Decorate, var.array_content_type, "ArrayStride", 32);
959 (op::Decorate, var.array_content_type, "ArrayStride", 16);
966 (op::Decorate, var.dataArrayType, "ArrayStride", 128);
970 // for matrices we use array of 4x4-component matrices
971 // stride of outer array is then 64 in every case
973 (op::Decorate, var.dataArrayType, "ArrayStride", 64);
978 (op::MemberDecorate, var.dataOutputType, 0, "Offset", 0)
979 (op::Decorate, var.dataOutputType, "Block")
981 // an input block. Marked readonly.
982 (op::MemberDecorate, var.dataInputType, 0, "NonWritable")
983 (op::MemberDecorate, var.dataInputType, 0, "Offset", 0)
984 (op::Decorate, var.dataInputType, "Block")
986 //a special structure matching data in one of our buffers.
987 // member at 0 is an index to read position
988 // member at 1 is an index to write position
989 // 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.
990 (op::MemberDecorate, var.dataSelectorStructType, 0, "Offset", 0)
991 (op::MemberDecorate, var.dataSelectorStructType, 1, "Offset", 4)
992 (op::MemberDecorate, var.dataSelectorStructType, 2, "Offset", 8)
993 (op::Decorate, var.dataSelectorStructType, "Block")
995 // binding to matching buffer
996 (op::Decorate, var.dataSelectorStructPtr, "DescriptorSet", 0)
997 (op::Decorate, var.dataSelectorStructPtr, "Binding", 2)
999 // making composite types used in shader
1000 (var.voidId, is, op::TypeVoid)
1001 (var.voidFuncVoid, is, op::TypeFunction, var.voidId)
1003 (var.boolean, is, op::TypeBool)
1005 (var.f32, is, op::TypeFloat, 32)
1006 (var.s32, is, op::TypeInt, 32, 1)
1007 (var.u32, is, op::TypeInt, 32, 0);
1012 (var.s64, is, op::TypeInt, 64, 1)
1013 (var.u64, is, op::TypeInt, 64, 0);
1017 (var.v4f32, is, op::TypeVector, var.f32, 4)
1018 (var.v4s32, is, op::TypeVector, var.s32, 4)
1019 (var.v4u32, is, op::TypeVector, var.u32, 4);
1024 (var.v4s64, is, op::TypeVector, var.s64, 4)
1025 (var.v4u64, is, op::TypeVector, var.u64, 4);
1028 // 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.
1029 // A Variable mappings will make sure the proper variable name is used
1030 // below is a first part of aliasing types based on int, uint, float
1031 switch (bufferFormat)
1033 case vk::VK_FORMAT_R32_SINT:
1034 shaderSource.makeSame(var.buffer_type, var.s32);
1035 shaderSource.makeSame(var.buffer_type_vec, var.v4s32);
1037 case vk::VK_FORMAT_R32_UINT:
1038 shaderSource.makeSame(var.buffer_type, var.u32);
1039 shaderSource.makeSame(var.buffer_type_vec, var.v4u32);
1041 case vk::VK_FORMAT_R32_SFLOAT:
1042 shaderSource.makeSame(var.buffer_type, var.f32);
1043 shaderSource.makeSame(var.buffer_type_vec, var.v4f32);
1045 case vk::VK_FORMAT_R64_SINT:
1046 shaderSource.makeSame(var.buffer_type, var.s64);
1047 shaderSource.makeSame(var.buffer_type_vec, var.v4s64);
1049 case vk::VK_FORMAT_R64_UINT:
1050 shaderSource.makeSame(var.buffer_type, var.u64);
1051 shaderSource.makeSame(var.buffer_type_vec, var.v4u64);
1054 // to prevent compiler from complaining not all cases are handled (but we should not get here).
1055 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__);
1059 // below is a second part that aliases based on scalar, vector, matrix
1062 case SHADER_TYPE_SCALAR_COPY:
1063 shaderSource.makeSame(var.copy_type, var.buffer_type);
1065 case SHADER_TYPE_VECTOR_COPY:
1066 shaderSource.makeSame(var.copy_type, var.buffer_type_vec);
1068 case SHADER_TYPE_MATRIX_COPY:
1069 if (bufferFormat != VK_FORMAT_R32_SFLOAT)
1070 TCU_THROW(NotSupportedError, "Matrices can be used only with floating point types.");
1072 (var.copy_type, is, op::TypeMatrix, var.buffer_type_vec, 4);
1075 // to prevent compiler from complaining not all cases are handled (but we should not get here).
1076 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__);
1080 // I will need some constants so lets add them to shader source
1082 (var.constants[0], is, op::Constant, var.s32, 0)
1083 (var.constants[1], is, op::Constant, var.s32, 1)
1084 (var.constants[2], is, op::Constant, var.s32, 2)
1085 (var.constants[3], is, op::Constant, var.s32, 3)
1086 (var.constants[4], is, op::Constant, var.u32, 4)
1087 (var.constants[5], is, op::Constant, var.u32, 1024);
1089 // for fragment shaders I need additionally a constant vector (output "colour") so lets make it
1090 if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
1093 (var.constants[6], is, op::Constant, var.f32, 1)
1094 (var.constants[7], is, op::ConstantComposite, var.v4f32, var.constants[6], var.constants[6], var.constants[6], var.constants[6]);
1097 // additional alias for the type of content of this 1024-element outer array.
1098 if (shaderType == SHADER_TYPE_SCALAR_COPY || shaderType == SHADER_TYPE_VECTOR_COPY)
1101 (var.array_content_type, is, op::TypeArray, var.buffer_type_vec, var.constants[4]);
1105 shaderSource.makeSame(var.array_content_type, var.copy_type);
1108 // Lets create pointer types to the input data type, output data type and a struct
1109 // This must be distinct types due to different type decorations
1110 // Lets make also actual poiters to the data
1112 (var.dataArrayType, is, op::TypeArray, var.array_content_type, var.constants[5])
1113 (var.dataInputType, is, op::TypeStruct, var.dataArrayType)
1114 (var.dataOutputType, is, op::TypeStruct, var.dataArrayType)
1115 (var.dataInputPtrType, is, op::TypePointer, "StorageBuffer", var.dataInputType)
1116 (var.dataOutputPtrType, is, op::TypePointer, "StorageBuffer", var.dataOutputType)
1117 (var.dataInput, is, op::Variable, var.dataInputPtrType, "StorageBuffer")
1118 (var.dataOutput, is, op::Variable, var.dataOutputPtrType, "StorageBuffer")
1119 (var.dataSelectorStructType, is, op::TypeStruct, var.s32, var.s32, var.s32)
1120 (var.dataSelectorStructPtrType, is, op::TypePointer, "Uniform", var.dataSelectorStructType)
1121 (var.dataSelectorStructPtr, is, op::Variable, var.dataSelectorStructPtrType, "Uniform");
1123 // we need also additional pointers to fullfil stage requirements on shaders inputs and outputs
1124 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
1127 (var.inputPtr, is, op::TypePointer, "Input", var.v4f32)
1128 (var.input, is, op::Variable, var.inputPtr, "Input")
1129 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32)
1130 (var.output, is, op::Variable, var.outputPtr, "Output");
1132 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
1135 (var.outputPtr, is, op::TypePointer, "Output", var.v4f32)
1136 (var.output, is, op::Variable, var.outputPtr, "Output");
1140 (var.copy_type_ptr, is, op::TypePointer, "StorageBuffer", var.copy_type)
1141 (var.s32_type_ptr, is, op::TypePointer, "Uniform", var.s32);
1143 // Make a shader main function
1145 (var.mainFunc, is, op::Function, var.voidId, "None", var.voidFuncVoid)
1146 (var.mainFuncLabel, is, op::Label);
1148 Variable copyFromPtr(localcounter), copyToPtr(localcounter), zeroPtr(localcounter);
1149 Variable copyFrom(localcounter), copyTo(localcounter), zero(localcounter);
1151 // Lets load data from our auxiliary buffer with reading index, writing index and zero.
1153 (copyToPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[1])
1154 (copyTo, is, op::Load, var.s32, copyToPtr)
1155 (copyFromPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[0])
1156 (copyFrom, is, op::Load, var.s32, copyFromPtr)
1157 (zeroPtr, is, op::AccessChain, var.s32_type_ptr, var.dataSelectorStructPtr, var.constants[2])
1158 (zero, is, op::Load, var.s32, zeroPtr);
1160 // let start copying data using variable pointers
1163 case SHADER_TYPE_SCALAR_COPY:
1164 for (int i = 0; i < 4; ++i)
1166 for (int j = 0; j < 4; ++j)
1168 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter);
1169 Variable selection(localcounter);
1170 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter);
1173 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]);
1177 // if we check reads we use variable pointers only for reading part
1179 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j])
1180 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j])
1181 // actualLoadChain will be a variable pointer as it was created through OpSelect
1182 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB)
1183 // actualStoreChain will be a regular pointer
1184 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j]);
1188 // if we check writes we use variable pointers only for writing part only
1190 // actualLoadChain will be regular regualar pointer
1191 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i], var.constants[j])
1192 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j])
1193 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i], var.constants[j])
1194 // actualStoreChain will be a variable pointer as it was created through OpSelect
1195 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB);
1197 // do actual copying
1199 (loadResult, is, op::Load, var.copy_type, actualLoadChain)
1200 (op::Store, actualStoreChain, loadResult);
1204 // cases below have the same logic as the one above - just we are copying bigger chunks of data with every load/store pair
1205 case SHADER_TYPE_VECTOR_COPY:
1206 for (int i = 0; i < 4; ++i)
1208 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter);
1209 Variable selection(localcounter);
1210 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter);
1213 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]);
1218 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i])
1219 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i])
1220 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB)
1221 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i]);
1226 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom, var.constants[i])
1227 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i])
1228 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo, var.constants[i])
1229 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB);
1233 (loadResult, is, op::Load, var.copy_type, actualLoadChain)
1234 (op::Store, actualStoreChain, loadResult);
1237 case SHADER_TYPE_MATRIX_COPY:
1239 Variable actualLoadChain(localcounter), actualStoreChain(localcounter), loadResult(localcounter);
1240 Variable selection(localcounter);
1241 Variable lcA(localcounter), lcB(localcounter), scA(localcounter), scB(localcounter);
1244 (selection, is, op::IEqual, var.boolean, zero, var.constants[0]);
1249 (lcA, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom)
1250 (lcB, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom)
1251 (actualLoadChain, is, op::Select, var.copy_type_ptr, selection, lcA, lcB)
1252 (actualStoreChain, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo);
1257 (actualLoadChain, is, op::AccessChain, var.copy_type_ptr, var.dataInput, var.constants[0], copyFrom)
1258 (scA, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo)
1259 (scB, is, op::AccessChain, var.copy_type_ptr, var.dataOutput, var.constants[0], copyTo)
1260 (actualStoreChain, is, op::Select, var.copy_type_ptr, selection, scA, scB);
1264 (loadResult, is, op::Load, var.copy_type, actualLoadChain)
1265 (op::Store, actualStoreChain, loadResult);
1269 // to prevent compiler from complaining not all cases are handled (but we should not get here).
1270 deAssertFail("This point should be not reachable with correct program flow.", __FILE__, __LINE__);
1275 // This is common for test shaders and unused ones
1276 // We need to fill stage ouput from shader properly
1277 // output vertices positions in vertex shader
1278 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
1280 Variable inputValue(localcounter), outputLocation(localcounter);
1282 (inputValue, is, op::Load, var.v4f32, var.input)
1283 (outputLocation, is, op::AccessChain, var.outputPtr, var.output)
1284 (op::Store, outputLocation, inputValue);
1286 // output colour in fragment shader
1287 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
1290 (op::Store, var.output, var.constants[7]);
1293 // We are done. Lets close main function body
1298 return shaderSource.str();
1301 RobustReadTest::RobustReadTest (tcu::TestContext& testContext,
1302 const std::string& name,
1303 const std::string& description,
1304 VkShaderStageFlags shaderStage,
1305 ShaderType shaderType,
1306 VkFormat bufferFormat,
1307 VkDeviceSize readAccessRange,
1308 bool accessOutOfBackingMemory)
1309 : RobustAccessWithPointersTest (testContext, name, description, shaderStage, shaderType, bufferFormat)
1310 , m_readAccessRange (readAccessRange)
1311 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
1315 TestInstance* RobustReadTest::createInstance (Context& context) const
1317 auto device = createRobustBufferAccessVariablePointersDevice(context);
1318 #ifndef CTS_USES_VULKANSC
1319 de::MovePtr<vk::DeviceDriver> deviceDriver = de::MovePtr<DeviceDriver>(new DeviceDriver(context.getPlatformInterface(), context.getInstance(), *device));
1321 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver = de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter>(new DeviceDriverSC(context.getPlatformInterface(), context.getInstance(), *device, context.getTestContext().getCommandLine(), context.getResourceInterface(), context.getDeviceVulkanSC10Properties(), context.getDeviceProperties()), vk::DeinitDeviceDeleter(context.getResourceInterface().get(), *device));
1322 #endif // CTS_USES_VULKANSC
1324 return new ReadInstance(context, device, deviceDriver, m_shaderType, m_shaderStage, m_bufferFormat, m_readAccessRange, m_accessOutOfBackingMemory);
1327 void RobustReadTest::initPrograms(SourceCollections& programCollection) const
1329 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
1331 programCollection.spirvAsmSources.add("compute") << MakeShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaderType, m_bufferFormat, true, false);
1335 programCollection.spirvAsmSources.add("vertex") << MakeShader(VK_SHADER_STAGE_VERTEX_BIT, m_shaderType, m_bufferFormat, true, m_shaderStage != VK_SHADER_STAGE_VERTEX_BIT);
1336 programCollection.spirvAsmSources.add("fragment") << MakeShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_shaderType, m_bufferFormat, true, m_shaderStage != VK_SHADER_STAGE_FRAGMENT_BIT);
1340 RobustWriteTest::RobustWriteTest (tcu::TestContext& testContext,
1341 const std::string& name,
1342 const std::string& description,
1343 VkShaderStageFlags shaderStage,
1344 ShaderType shaderType,
1345 VkFormat bufferFormat,
1346 VkDeviceSize writeAccessRange,
1347 bool accessOutOfBackingMemory)
1349 : RobustAccessWithPointersTest (testContext, name, description, shaderStage, shaderType, bufferFormat)
1350 , m_writeAccessRange (writeAccessRange)
1351 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
1355 TestInstance* RobustWriteTest::createInstance (Context& context) const
1357 auto device = createRobustBufferAccessVariablePointersDevice(context);
1358 #ifndef CTS_USES_VULKANSC
1359 de::MovePtr<vk::DeviceDriver> deviceDriver = de::MovePtr<DeviceDriver>(new DeviceDriver(context.getPlatformInterface(), context.getInstance(), *device));
1361 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver = de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter>(new DeviceDriverSC(context.getPlatformInterface(), context.getInstance(), *device, context.getTestContext().getCommandLine(), context.getResourceInterface(), context.getDeviceVulkanSC10Properties(), context.getDeviceProperties()), vk::DeinitDeviceDeleter(context.getResourceInterface().get(), *device));
1362 #endif // CTS_USES_VULKANSC
1364 return new WriteInstance(context, device, deviceDriver, m_shaderType, m_shaderStage, m_bufferFormat, m_writeAccessRange, m_accessOutOfBackingMemory);
1367 void RobustWriteTest::initPrograms(SourceCollections& programCollection) const
1369 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
1371 programCollection.spirvAsmSources.add("compute") << MakeShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaderType, m_bufferFormat, false, false);
1375 programCollection.spirvAsmSources.add("vertex") << MakeShader(VK_SHADER_STAGE_VERTEX_BIT, m_shaderType, m_bufferFormat, false, m_shaderStage != VK_SHADER_STAGE_VERTEX_BIT);
1376 programCollection.spirvAsmSources.add("fragment") << MakeShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_shaderType, m_bufferFormat, false, m_shaderStage != VK_SHADER_STAGE_FRAGMENT_BIT);
1380 AccessInstance::AccessInstance (Context& context,
1381 Move<VkDevice> device,
1382 #ifndef CTS_USES_VULKANSC
1383 de::MovePtr<vk::DeviceDriver> deviceDriver,
1385 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
1386 #endif // CTS_USES_VULKANSC
1388 ShaderType shaderType,
1389 VkShaderStageFlags shaderStage,
1390 VkFormat bufferFormat,
1391 BufferAccessType bufferAccessType,
1392 VkDeviceSize inBufferAccessRange,
1393 VkDeviceSize outBufferAccessRange,
1394 bool accessOutOfBackingMemory)
1395 : vkt::TestInstance (context)
1397 , m_deviceDriver (deviceDriver)
1398 , m_shaderType (shaderType)
1399 , m_shaderStage (shaderStage)
1400 , m_bufferFormat (bufferFormat)
1401 , m_bufferAccessType (bufferAccessType)
1402 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
1404 tcu::TestLog& log = context.getTestContext().getLog();
1405 const DeviceInterface& vk = *m_deviceDriver;
1406 const auto& vki = context.getInstanceInterface();
1407 const auto instance = context.getInstance();
1408 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
1409 const VkPhysicalDevice physicalDevice = chooseDevice(vki, instance, context.getTestContext().getCommandLine());
1410 SimpleAllocator memAlloc (vk, *m_device, getPhysicalDeviceMemoryProperties(vki, physicalDevice));
1412 DE_ASSERT(RobustAccessWithPointersTest::s_numberOfBytesAccessed % sizeof(deUint32) == 0);
1413 DE_ASSERT(inBufferAccessRange <= RobustAccessWithPointersTest::s_numberOfBytesAccessed);
1414 DE_ASSERT(outBufferAccessRange <= RobustAccessWithPointersTest::s_numberOfBytesAccessed);
1416 if (m_bufferFormat == VK_FORMAT_R64_UINT || m_bufferFormat == VK_FORMAT_R64_SINT)
1418 context.requireDeviceFunctionality("VK_EXT_shader_image_atomic_int64");
1421 // Check storage support
1422 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
1424 if (!context.getDeviceFeatures().vertexPipelineStoresAndAtomics)
1426 TCU_THROW(NotSupportedError, "Stores not supported in vertex stage");
1429 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
1431 if (!context.getDeviceFeatures().fragmentStoresAndAtomics)
1433 TCU_THROW(NotSupportedError, "Stores not supported in fragment stage");
1437 createTestBuffer(context, vk, *m_device, inBufferAccessRange, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memAlloc, m_inBuffer, m_inBufferAlloc, m_inBufferAccess, &populateBufferWithValues, &m_bufferFormat);
1438 createTestBuffer(context, vk, *m_device, outBufferAccessRange, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, memAlloc, m_outBuffer, m_outBufferAlloc, m_outBufferAccess, &populateBufferWithFiller, DE_NULL);
1440 deInt32 indices[] = {
1441 (m_accessOutOfBackingMemory && (m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE)) ? static_cast<deInt32>(RobustAccessWithPointersTest::s_testArraySize) - 1 : 0,
1442 (m_accessOutOfBackingMemory && (m_bufferAccessType == BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE)) ? static_cast<deInt32>(RobustAccessWithPointersTest::s_testArraySize) - 1 : 0,
1445 AccessRangesData indicesAccess;
1446 createTestBuffer(context, vk, *m_device, 3 * sizeof(deInt32), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, memAlloc, m_indicesBuffer, m_indicesBufferAlloc, indicesAccess, &populateBufferWithCopy, &indices);
1448 log << tcu::TestLog::Message << "input buffer - alloc size: " << m_inBufferAccess.allocSize << tcu::TestLog::EndMessage;
1449 log << tcu::TestLog::Message << "input buffer - max access range: " << m_inBufferAccess.maxAccessRange << tcu::TestLog::EndMessage;
1450 log << tcu::TestLog::Message << "output buffer - alloc size: " << m_outBufferAccess.allocSize << tcu::TestLog::EndMessage;
1451 log << tcu::TestLog::Message << "output buffer - max access range: " << m_outBufferAccess.maxAccessRange << tcu::TestLog::EndMessage;
1452 log << tcu::TestLog::Message << "indices - input offset: " << indices[0] << tcu::TestLog::EndMessage;
1453 log << tcu::TestLog::Message << "indices - output offset: " << indices[1] << tcu::TestLog::EndMessage;
1454 log << tcu::TestLog::Message << "indices - additional: " << indices[2] << tcu::TestLog::EndMessage;
1456 // Create descriptor data
1458 DescriptorPoolBuilder descriptorPoolBuilder;
1459 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
1460 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
1461 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u);
1462 m_descriptorPool = descriptorPoolBuilder.build(vk, *m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
1464 DescriptorSetLayoutBuilder setLayoutBuilder;
1465 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_ALL);
1466 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_ALL);
1467 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL);
1468 m_descriptorSetLayout = setLayoutBuilder.build(vk, *m_device);
1470 const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo =
1472 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
1473 DE_NULL, // const void* pNext;
1474 *m_descriptorPool, // VkDescriptorPool descriptorPool;
1475 1u, // deUint32 setLayoutCount;
1476 &m_descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts;
1479 m_descriptorSet = allocateDescriptorSet(vk, *m_device, &descriptorSetAllocateInfo);
1481 const VkDescriptorBufferInfo inBufferDescriptorInfo = makeDescriptorBufferInfo(*m_inBuffer, 0ull, m_inBufferAccess.accessRange);
1482 const VkDescriptorBufferInfo outBufferDescriptorInfo = makeDescriptorBufferInfo(*m_outBuffer, 0ull, m_outBufferAccess.accessRange);
1483 const VkDescriptorBufferInfo indicesBufferDescriptorInfo = makeDescriptorBufferInfo(*m_indicesBuffer, 0ull, 12ull);
1485 DescriptorSetUpdateBuilder setUpdateBuilder;
1486 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &inBufferDescriptorInfo);
1487 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outBufferDescriptorInfo);
1488 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &indicesBufferDescriptorInfo);
1489 setUpdateBuilder.update(vk, *m_device);
1494 const VkFenceCreateInfo fenceParams =
1496 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
1497 DE_NULL, // const void* pNext;
1498 0u // VkFenceCreateFlags flags;
1501 m_fence = createFence(vk, *m_device, &fenceParams);
1505 vk.getDeviceQueue(*m_device, queueFamilyIndex, 0, &m_queue);
1507 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
1509 m_testEnvironment = de::MovePtr<TestEnvironment>(new ComputeEnvironment(m_context, *m_deviceDriver, *m_device, *m_descriptorSetLayout, *m_descriptorSet));
1515 const VkVertexInputBindingDescription vertexInputBindingDescription =
1517 0u, // deUint32 binding;
1518 sizeof(tcu::Vec4), // deUint32 strideInBytes;
1519 VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
1522 const VkVertexInputAttributeDescription vertexInputAttributeDescription =
1524 0u, // deUint32 location;
1525 0u, // deUint32 binding;
1526 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
1527 0u // deUint32 offset;
1530 AccessRangesData vertexAccess;
1531 const Vec4 vertices[] =
1533 Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
1534 Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
1535 Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
1537 const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(sizeof(vertices));
1538 createTestBuffer(context, vk, *m_device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, memAlloc, m_vertexBuffer, m_vertexBufferAlloc, vertexAccess, &populateBufferWithCopy, &vertices);
1540 const GraphicsEnvironment::DrawConfig drawWithOneVertexBuffer =
1542 std::vector<VkBuffer>(1, *m_vertexBuffer), // std::vector<VkBuffer> vertexBuffers;
1543 DE_LENGTH_OF_ARRAY(vertices), // deUint32 vertexCount;
1544 1, // deUint32 instanceCount;
1545 DE_NULL, // VkBuffer indexBuffer;
1546 0u, // deUint32 indexCount;
1549 m_testEnvironment = de::MovePtr<TestEnvironment>(new GraphicsEnvironment(m_context,
1552 *m_descriptorSetLayout,
1554 GraphicsEnvironment::VertexBindings(1, vertexInputBindingDescription),
1555 GraphicsEnvironment::VertexAttributes(1, vertexInputAttributeDescription),
1556 drawWithOneVertexBuffer));
1560 AccessInstance::~AccessInstance()
1564 // Verifies if the buffer has the value initialized by BufferAccessInstance::populateReadBuffer at a given offset.
1565 bool AccessInstance::isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes,
1566 const void* valuePtr,
1567 VkDeviceSize valueSize)
1569 DE_ASSERT(offsetInBytes % 4 == 0);
1570 DE_ASSERT(offsetInBytes < m_inBufferAccess.allocSize);
1571 DE_ASSERT(valueSize == 4ull || valueSize == 8ull);
1573 const deUint32 valueIndex = deUint32(offsetInBytes / 4) + 2;
1575 if (isUintFormat(m_bufferFormat))
1577 const deUint32 expectedValues[2] = { valueIndex, valueIndex + 1u };
1578 return !deMemCmp(valuePtr, &expectedValues, (size_t)valueSize);
1580 else if (isIntFormat(m_bufferFormat))
1582 const deInt32 value = -deInt32(valueIndex);
1583 const deInt32 expectedValues[2] = { value, value - 1 };
1584 return !deMemCmp(valuePtr, &expectedValues, (size_t)valueSize);
1586 else if (isFloatFormat(m_bufferFormat))
1588 DE_ASSERT(valueSize == 4ull);
1589 const float value = float(valueIndex);
1590 return !deMemCmp(valuePtr, &value, (size_t)valueSize);
1599 bool AccessInstance::isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize)
1601 DE_ASSERT(valueSize <= 8);
1602 const deUint8 *const outValuePtr = (deUint8*)m_outBufferAlloc->getHostPtr() + offsetInBytes;
1603 const deUint64 defaultValue = 0xBABABABABABABABAull;
1605 return !deMemCmp(outValuePtr, &defaultValue, (size_t)valueSize);
1608 tcu::TestStatus AccessInstance::iterate (void)
1610 const DeviceInterface& vk = *m_deviceDriver;
1611 const vk::VkCommandBuffer cmdBuffer = m_testEnvironment->getCommandBuffer();
1613 // Submit command buffer
1615 const VkSubmitInfo submitInfo =
1617 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
1618 DE_NULL, // const void* pNext;
1619 0u, // deUint32 waitSemaphoreCount;
1620 DE_NULL, // const VkSemaphore* pWaitSemaphores;
1621 DE_NULL, // const VkPIpelineStageFlags* pWaitDstStageMask;
1622 1u, // deUint32 commandBufferCount;
1623 &cmdBuffer, // const VkCommandBuffer* pCommandBuffers;
1624 0u, // deUint32 signalSemaphoreCount;
1625 DE_NULL // const VkSemaphore* pSignalSemaphores;
1628 VK_CHECK(vk.resetFences(*m_device, 1, &m_fence.get()));
1629 VK_CHECK(vk.queueSubmit(m_queue, 1, &submitInfo, *m_fence));
1630 VK_CHECK(vk.waitForFences(*m_device, 1, &m_fence.get(), true, ~(0ull) /* infinity */));
1633 // Prepare result buffer for read
1635 const VkMappedMemoryRange outBufferRange =
1637 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType;
1638 DE_NULL, // const void* pNext;
1639 m_outBufferAlloc->getMemory(), // VkDeviceMemory mem;
1640 0ull, // VkDeviceSize offset;
1641 m_outBufferAccess.allocSize, // VkDeviceSize size;
1644 VK_CHECK(vk.invalidateMappedMemoryRanges(*m_device, 1u, &outBufferRange));
1648 return tcu::TestStatus::pass("All values OK");
1650 return tcu::TestStatus::fail("Invalid value(s) found");
1653 bool AccessInstance::verifyResult (bool splitAccess)
1655 std::ostringstream logMsg;
1656 tcu::TestLog& log = m_context.getTestContext().getLog();
1657 const bool isReadAccess = (m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE);
1658 const void* inDataPtr = m_inBufferAlloc->getHostPtr();
1659 const void* outDataPtr = m_outBufferAlloc->getHostPtr();
1661 deUint32 valueNdx = 0;
1662 const VkDeviceSize maxAccessRange = isReadAccess ? m_inBufferAccess.maxAccessRange : m_outBufferAccess.maxAccessRange;
1663 const bool isR64 = (m_bufferFormat == VK_FORMAT_R64_UINT || m_bufferFormat == VK_FORMAT_R64_SINT);
1664 const deUint32 unsplitElementSize = (isR64 ? 8u : 4u);
1665 const deUint32 elementSize = ((isR64 && !splitAccess) ? 8u : 4u);
1667 for (VkDeviceSize offsetInBytes = 0; offsetInBytes < m_outBufferAccess.allocSize; offsetInBytes += elementSize)
1669 const deUint8* outValuePtr = static_cast<const deUint8*>(outDataPtr) + offsetInBytes;
1670 const size_t outValueSize = static_cast<size_t>(deMinu64(elementSize, (m_outBufferAccess.allocSize - offsetInBytes)));
1672 if (offsetInBytes >= RobustAccessWithPointersTest::s_numberOfBytesAccessed)
1674 // The shader will only write 16 values into the result buffer. The rest of the values
1675 // should remain unchanged or may be modified if we are writing out of bounds.
1676 if (!isOutBufferValueUnchanged(offsetInBytes, outValueSize)
1677 && (isReadAccess || !isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, 4)))
1679 logMsg << "\nValue " << valueNdx++ << " has been modified with an unknown value: " << *(static_cast<const deUint32*>(static_cast<const void*>(outValuePtr)));
1685 const deInt32 distanceToOutOfBounds = static_cast<deInt32>(maxAccessRange) - static_cast<deInt32>(offsetInBytes);
1686 bool isOutOfBoundsAccess = false;
1688 logMsg << "\n" << valueNdx++ << ": ";
1690 logValue(logMsg, outValuePtr, m_bufferFormat, outValueSize);
1692 if (m_accessOutOfBackingMemory)
1693 isOutOfBoundsAccess = true;
1695 // Check if the shader operation accessed an operand located less than 16 bytes away
1696 // from the out of bounds address. Less than 32 bytes away for 64 bit accesses.
1697 if (!isOutOfBoundsAccess && distanceToOutOfBounds < (isR64 ? 32 : 16))
1699 deUint32 operandSize = 0;
1701 switch (m_shaderType)
1703 case SHADER_TYPE_SCALAR_COPY:
1704 operandSize = unsplitElementSize; // Size of scalar
1707 case SHADER_TYPE_VECTOR_COPY:
1708 operandSize = unsplitElementSize * 4; // Size of vec4
1711 case SHADER_TYPE_MATRIX_COPY:
1712 operandSize = unsplitElementSize * 16; // Size of mat4
1719 isOutOfBoundsAccess = (((offsetInBytes / operandSize) + 1) * operandSize > maxAccessRange);
1722 if (isOutOfBoundsAccess)
1724 logMsg << " (out of bounds " << (isReadAccess ? "read": "write") << ")";
1726 const bool isValuePartiallyOutOfBounds = ((distanceToOutOfBounds > 0) && ((deUint32)distanceToOutOfBounds < elementSize));
1727 bool isValidValue = false;
1729 if (isValuePartiallyOutOfBounds && !m_accessOutOfBackingMemory)
1731 // The value is partially out of bounds
1733 bool isOutOfBoundsPartOk = true;
1734 bool isWithinBoundsPartOk = true;
1736 deUint32 inBoundPartSize = distanceToOutOfBounds;
1738 // For cases that partial element is out of bound, the part within the buffer allocated memory can be buffer content per spec.
1739 // We need to check it as a whole part.
1740 if (offsetInBytes + elementSize > m_inBufferAccess.allocSize)
1742 inBoundPartSize = static_cast<deInt32>(m_inBufferAccess.allocSize) - static_cast<deInt32>(offsetInBytes);
1747 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, inBoundPartSize);
1748 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, (deUint8*)outValuePtr + inBoundPartSize, outValueSize - inBoundPartSize);
1752 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, inBoundPartSize)
1753 || isOutBufferValueUnchanged(offsetInBytes, inBoundPartSize);
1755 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, (deUint8*)outValuePtr + inBoundPartSize, outValueSize - inBoundPartSize)
1756 || isOutBufferValueUnchanged(offsetInBytes + inBoundPartSize, outValueSize - inBoundPartSize);
1759 logMsg << ", first " << distanceToOutOfBounds << " byte(s) " << (isWithinBoundsPartOk ? "OK": "wrong");
1760 logMsg << ", last " << outValueSize - distanceToOutOfBounds << " byte(s) " << (isOutOfBoundsPartOk ? "OK": "wrong");
1762 isValidValue = isWithinBoundsPartOk && isOutOfBoundsPartOk;
1768 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, outValueSize);
1772 isValidValue = isOutBufferValueUnchanged(offsetInBytes, outValueSize);
1776 // Out of bounds writes may modify values withing the memory ranges bound to the buffer
1777 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.allocSize, outValuePtr, outValueSize);
1780 logMsg << ", OK, written within the memory range bound to the buffer";
1785 if (!isValidValue && !splitAccess)
1787 // Check if we are satisfying the [0, 0, 0, x] pattern, where x may be either 0 or 1,
1788 // or the maximum representable positive integer value (if the format is integer-based).
1790 const bool canMatchVec4Pattern = (isReadAccess
1791 && !isValuePartiallyOutOfBounds
1792 && (m_shaderType == SHADER_TYPE_VECTOR_COPY)
1793 && (offsetInBytes / elementSize + 1) % 4 == 0);
1794 bool matchesVec4Pattern = false;
1796 if (canMatchVec4Pattern)
1798 matchesVec4Pattern = verifyOutOfBoundsVec4(outValuePtr - 3u * elementSize, m_bufferFormat);
1801 if (!canMatchVec4Pattern || !matchesVec4Pattern)
1803 logMsg << ". Failed: ";
1807 logMsg << "expected value within the buffer range or 0";
1809 if (canMatchVec4Pattern)
1810 logMsg << ", or the [0, 0, 0, x] pattern";
1814 logMsg << "written out of the range";
1821 else // We are within bounds
1825 if (!isExpectedValueFromInBuffer(offsetInBytes, outValuePtr, elementSize))
1827 logMsg << ", Failed: unexpected value";
1833 // Out of bounds writes may change values within the bounds.
1834 if (!isValueWithinBufferOrZero(inDataPtr, m_inBufferAccess.accessRange, outValuePtr, elementSize))
1836 logMsg << ", Failed: unexpected value";
1844 log << tcu::TestLog::Message << logMsg.str() << tcu::TestLog::EndMessage;
1846 if (!allOk && unsplitElementSize > 4u && !splitAccess)
1848 // "Non-atomic accesses to storage buffers that are a multiple of 32 bits may be decomposed into 32-bit accesses that are individually bounds-checked."
1849 return verifyResult(true/*splitAccess*/);
1855 // BufferReadInstance
1857 ReadInstance::ReadInstance (Context& context,
1858 Move<VkDevice> device,
1859 #ifndef CTS_USES_VULKANSC
1860 de::MovePtr<vk::DeviceDriver> deviceDriver,
1862 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
1863 #endif // CTS_USES_VULKANSC
1864 ShaderType shaderType,
1865 VkShaderStageFlags shaderStage,
1866 VkFormat bufferFormat,
1867 //bool readFromStorage,
1868 VkDeviceSize inBufferAccessRange,
1869 bool accessOutOfBackingMemory)
1871 : AccessInstance (context, device, deviceDriver, shaderType, shaderStage, bufferFormat,
1872 BUFFER_ACCESS_TYPE_READ_FROM_STORAGE,
1873 inBufferAccessRange, RobustAccessWithPointersTest::s_numberOfBytesAccessed,
1874 accessOutOfBackingMemory)
1878 // BufferWriteInstance
1880 WriteInstance::WriteInstance (Context& context,
1881 Move<VkDevice> device,
1882 #ifndef CTS_USES_VULKANSC
1883 de::MovePtr<vk::DeviceDriver> deviceDriver,
1885 de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> deviceDriver,
1886 #endif // CTS_USES_VULKANSC
1887 ShaderType shaderType,
1888 VkShaderStageFlags shaderStage,
1889 VkFormat bufferFormat,
1890 VkDeviceSize writeBufferAccessRange,
1891 bool accessOutOfBackingMemory)
1893 : AccessInstance (context, device, deviceDriver, shaderType, shaderStage, bufferFormat,
1894 BUFFER_ACCESS_TYPE_WRITE_TO_STORAGE,
1895 RobustAccessWithPointersTest::s_numberOfBytesAccessed, writeBufferAccessRange,
1896 accessOutOfBackingMemory)
1900 } // unnamed namespace
1902 tcu::TestCaseGroup* createBufferAccessWithVariablePointersTests(tcu::TestContext& testCtx)
1904 // Lets make group for the tests
1905 de::MovePtr<tcu::TestCaseGroup> bufferAccessWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "through_pointers", ""));
1907 // Lets add subgroups to better organise tests
1908 de::MovePtr<tcu::TestCaseGroup> computeWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "compute", ""));
1909 de::MovePtr<tcu::TestCaseGroup> computeReads (new tcu::TestCaseGroup(testCtx, "reads", ""));
1910 de::MovePtr<tcu::TestCaseGroup> computeWrites (new tcu::TestCaseGroup(testCtx, "writes", ""));
1912 de::MovePtr<tcu::TestCaseGroup> graphicsWithVariablePointersTests (new tcu::TestCaseGroup(testCtx, "graphics", ""));
1913 de::MovePtr<tcu::TestCaseGroup> graphicsReads (new tcu::TestCaseGroup(testCtx, "reads", ""));
1914 de::MovePtr<tcu::TestCaseGroup> graphicsReadsVertex (new tcu::TestCaseGroup(testCtx, "vertex", ""));
1915 de::MovePtr<tcu::TestCaseGroup> graphicsReadsFragment (new tcu::TestCaseGroup(testCtx, "fragment", ""));
1916 de::MovePtr<tcu::TestCaseGroup> graphicsWrites (new tcu::TestCaseGroup(testCtx, "writes", ""));
1917 de::MovePtr<tcu::TestCaseGroup> graphicsWritesVertex (new tcu::TestCaseGroup(testCtx, "vertex", ""));
1918 de::MovePtr<tcu::TestCaseGroup> graphicsWritesFragment (new tcu::TestCaseGroup(testCtx, "fragment", ""));
1920 // A struct for describing formats
1923 const VkFormat value;
1924 const char * const name;
1927 const Formats bufferFormats[] =
1929 { VK_FORMAT_R32_SINT, "s32" },
1930 { VK_FORMAT_R32_UINT, "u32" },
1931 { VK_FORMAT_R32_SFLOAT, "f32" },
1932 { VK_FORMAT_R64_SINT, "s64" },
1933 { VK_FORMAT_R64_UINT, "u64" },
1935 const deUint8 bufferFormatsCount = static_cast<deUint8>(DE_LENGTH_OF_ARRAY(bufferFormats));
1937 // Amounts of data to copy
1938 const VkDeviceSize rangeSizes[] =
1940 1ull, 3ull, 4ull, 16ull, 32ull
1942 const deUint8 rangeSizesCount = static_cast<deUint8>(DE_LENGTH_OF_ARRAY(rangeSizes));
1944 // gather above data into one array
1945 const struct ShaderTypes
1947 const ShaderType value;
1948 const char * const name;
1949 const Formats* const formats;
1950 const deUint8 formatsCount;
1951 const VkDeviceSize* const sizes;
1952 const deUint8 sizesCount;
1955 { SHADER_TYPE_VECTOR_COPY, "vec4", bufferFormats, bufferFormatsCount, rangeSizes, rangeSizesCount },
1956 { SHADER_TYPE_SCALAR_COPY, "scalar", bufferFormats, bufferFormatsCount, rangeSizes, rangeSizesCount }
1959 // Specify to which subgroups put various tests
1960 const struct ShaderStages
1962 VkShaderStageFlags stage;
1963 de::MovePtr<tcu::TestCaseGroup>& reads;
1964 de::MovePtr<tcu::TestCaseGroup>& writes;
1967 { VK_SHADER_STAGE_VERTEX_BIT, graphicsReadsVertex, graphicsWritesVertex },
1968 { VK_SHADER_STAGE_FRAGMENT_BIT, graphicsReadsFragment, graphicsWritesFragment },
1969 { VK_SHADER_STAGE_COMPUTE_BIT, computeReads, computeWrites }
1972 // Eventually specify if memory used should be in the "inaccesible" portion of buffer or entirely outside of buffer
1973 const char* const backingMemory[] = { "in_memory", "out_of_memory" };
1975 for (deInt32 stageId = 0; stageId < DE_LENGTH_OF_ARRAY(stages); ++stageId)
1976 for (int i = 0; i < DE_LENGTH_OF_ARRAY(types); ++i)
1977 for (int j = 0; j < types[i].formatsCount; ++j)
1978 for (int k = 0; k < types[i].sizesCount; ++k)
1979 for (int s = 0; s < DE_LENGTH_OF_ARRAY(backingMemory); ++s)
1981 std::ostringstream name;
1982 name << types[i].sizes[k] << "B_" << backingMemory[s] << "_with_" << types[i].name << '_' << types[i].formats[j].name;
1983 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));
1986 for (deInt32 stageId = 0; stageId < DE_LENGTH_OF_ARRAY(stages); ++stageId)
1987 for (int i=0; i<DE_LENGTH_OF_ARRAY(types); ++i)
1988 for (int j=0; j<types[i].formatsCount; ++j)
1989 for (int k = 0; k<types[i].sizesCount; ++k)
1990 for (int s = 0; s < DE_LENGTH_OF_ARRAY(backingMemory); ++s)
1992 std::ostringstream name;
1993 name << types[i].sizes[k] << "B_" << backingMemory[s] << "_with_" << types[i].name << '_' << types[i].formats[j].name;
1994 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));
1997 graphicsReads->addChild(graphicsReadsVertex.release());
1998 graphicsReads->addChild(graphicsReadsFragment.release());
2000 graphicsWrites->addChild(graphicsWritesVertex.release());
2001 graphicsWrites->addChild(graphicsWritesFragment.release());
2003 graphicsWithVariablePointersTests->addChild(graphicsReads.release());
2004 graphicsWithVariablePointersTests->addChild(graphicsWrites.release());
2006 computeWithVariablePointersTests->addChild(computeReads.release());
2007 computeWithVariablePointersTests->addChild(computeWrites.release());
2009 bufferAccessWithVariablePointersTests->addChild(graphicsWithVariablePointersTests.release());
2010 bufferAccessWithVariablePointersTests->addChild(computeWithVariablePointersTests.release());
2012 return bufferAccessWithVariablePointersTests.release();