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