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