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