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