1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
6 * Copyright (c) 2016 Imagination Technologies Ltd.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Robust buffer access tests for uniform/storage buffers and
23 * uniform/storage texel buffers.
24 *//*--------------------------------------------------------------------*/
25
26 #include "vktRobustnessBufferAccessTests.hpp"
27 #include "vktRobustnessUtil.hpp"
28 #include "vktTestCaseUtil.hpp"
29 #include "vkBuilderUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkPrograms.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkRef.hpp"
34 #include "vkRefUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "tcuTestLog.hpp"
37
38 #include <limits>
39 #include <sstream>
40
41 namespace vkt
42 {
43 namespace robustness
44 {
45
46 using namespace vk;
47
48 enum ShaderType
49 {
50 SHADER_TYPE_MATRIX_COPY,
51 SHADER_TYPE_VECTOR_COPY,
52 SHADER_TYPE_SCALAR_COPY,
53 SHADER_TYPE_TEXEL_COPY,
54
55 SHADER_TYPE_COUNT
56 };
57
58 enum BufferAccessType
59 {
60 BUFFER_ACCESS_TYPE_READ,
61 BUFFER_ACCESS_TYPE_READ_FROM_STORAGE,
62 BUFFER_ACCESS_TYPE_WRITE,
63 };
64
min(VkDeviceSize a,VkDeviceSize b)65 static VkDeviceSize min (VkDeviceSize a, VkDeviceSize b)
66 {
67 return (a < b) ? a : b;
68 }
69
70 class RobustBufferAccessTest : public vkt::TestCase
71 {
72 public:
73 static const deUint32 s_testArraySize;
74 static const deUint32 s_numberOfBytesAccessed;
75
76 RobustBufferAccessTest (tcu::TestContext& testContext,
77 const std::string& name,
78 const std::string& description,
79 VkShaderStageFlags shaderStage,
80 ShaderType shaderType,
81 VkFormat bufferFormat);
82
~RobustBufferAccessTest(void)83 virtual ~RobustBufferAccessTest (void) {}
84
85 virtual void checkSupport (Context& context) const;
86
87 private:
88 static void genBufferShaderAccess (ShaderType shaderType,
89 VkFormat bufferFormat,
90 bool readFromStorage,
91 std::ostringstream& bufferDefinition,
92 std::ostringstream& bufferUse);
93
94 static void genTexelBufferShaderAccess (VkFormat bufferFormat,
95 std::ostringstream& bufferDefinition,
96 std::ostringstream& bufferUse,
97 bool readFromStorage);
98
99 protected:
100 static void initBufferAccessPrograms (SourceCollections& programCollection,
101 VkShaderStageFlags shaderStage,
102 ShaderType shaderType,
103 VkFormat bufferFormat,
104 bool readFromStorage);
105
106 const VkShaderStageFlags m_shaderStage;
107 const ShaderType m_shaderType;
108 const VkFormat m_bufferFormat;
109 };
110
111 class RobustBufferReadTest : public RobustBufferAccessTest
112 {
113 public:
114 RobustBufferReadTest (tcu::TestContext& testContext,
115 const std::string& name,
116 const std::string& description,
117 VkShaderStageFlags shaderStage,
118 ShaderType shaderType,
119 VkFormat bufferFormat,
120 VkDeviceSize readAccessRange,
121 bool readFromStorage,
122 bool accessOutOfBackingMemory);
123
~RobustBufferReadTest(void)124 virtual ~RobustBufferReadTest (void) {}
125
126 virtual void initPrograms (SourceCollections& programCollection) const;
127 virtual TestInstance* createInstance (Context& context) const;
128
129 private:
130 const bool m_readFromStorage;
131 const VkDeviceSize m_readAccessRange;
132 const bool m_accessOutOfBackingMemory;
133 };
134
135 class RobustBufferWriteTest : public RobustBufferAccessTest
136 {
137 public:
138 RobustBufferWriteTest (tcu::TestContext& testContext,
139 const std::string& name,
140 const std::string& description,
141 VkShaderStageFlags shaderStage,
142 ShaderType shaderType,
143 VkFormat bufferFormat,
144 VkDeviceSize writeAccessRange,
145 bool accessOutOfBackingMemory);
146
~RobustBufferWriteTest(void)147 virtual ~RobustBufferWriteTest (void) {}
148
149 virtual void initPrograms (SourceCollections& programCollection) const;
150 virtual TestInstance* createInstance (Context& context) const;
151
152 private:
153 const VkDeviceSize m_writeAccessRange;
154 const bool m_accessOutOfBackingMemory;
155 };
156
157 class BufferAccessInstance : public vkt::TestInstance
158 {
159 public:
160 BufferAccessInstance (Context& context,
161 Move<VkDevice> device,
162 ShaderType shaderType,
163 VkShaderStageFlags shaderStage,
164 VkFormat bufferFormat,
165 BufferAccessType bufferAccessType,
166 VkDeviceSize inBufferAccessRange,
167 VkDeviceSize outBufferAccessRange,
168 bool accessOutOfBackingMemory);
169
~BufferAccessInstance(void)170 virtual ~BufferAccessInstance (void) {}
171
172 virtual tcu::TestStatus iterate (void);
173
174 virtual bool verifyResult (void);
175
176 private:
177 bool isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, const void* valuePtr, VkDeviceSize valueSize);
178 bool isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize);
179
180 protected:
181 Move<VkDevice> m_device;
182 de::MovePtr<TestEnvironment> m_testEnvironment;
183
184 const ShaderType m_shaderType;
185 const VkShaderStageFlags m_shaderStage;
186
187 const VkFormat m_bufferFormat;
188 const BufferAccessType m_bufferAccessType;
189
190 const VkDeviceSize m_inBufferAccessRange;
191 Move<VkBuffer> m_inBuffer;
192 de::MovePtr<Allocation> m_inBufferAlloc;
193 VkDeviceSize m_inBufferAllocSize;
194 VkDeviceSize m_inBufferMaxAccessRange;
195
196 const VkDeviceSize m_outBufferAccessRange;
197 Move<VkBuffer> m_outBuffer;
198 de::MovePtr<Allocation> m_outBufferAlloc;
199 VkDeviceSize m_outBufferAllocSize;
200 VkDeviceSize m_outBufferMaxAccessRange;
201
202 Move<VkBuffer> m_indicesBuffer;
203 de::MovePtr<Allocation> m_indicesBufferAlloc;
204
205 Move<VkDescriptorPool> m_descriptorPool;
206 Move<VkDescriptorSetLayout> m_descriptorSetLayout;
207 Move<VkDescriptorSet> m_descriptorSet;
208
209 Move<VkFence> m_fence;
210 VkQueue m_queue;
211
212 // Used when m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT
213 Move<VkBuffer> m_vertexBuffer;
214 de::MovePtr<Allocation> m_vertexBufferAlloc;
215
216 // Used when m_shaderType == SHADER_TYPE_TEXEL_COPY
217 Move<VkBufferView> m_inTexelBufferView;
218 Move<VkBufferView> m_outTexelBufferView;
219
220 const bool m_accessOutOfBackingMemory;
221 };
222
223 class BufferReadInstance: public BufferAccessInstance
224 {
225 public:
226 BufferReadInstance (Context& context,
227 Move<VkDevice> device,
228 ShaderType shaderType,
229 VkShaderStageFlags shaderStage,
230 VkFormat bufferFormat,
231 bool readFromStorage,
232 VkDeviceSize inBufferAccessRange,
233 bool accessOutOfBackingMemory);
234
~BufferReadInstance(void)235 virtual ~BufferReadInstance (void) {}
236
237 private:
238 };
239
240 class BufferWriteInstance: public BufferAccessInstance
241 {
242 public:
243 BufferWriteInstance (Context& context,
244 Move<VkDevice> device,
245 ShaderType shaderType,
246 VkShaderStageFlags shaderStage,
247 VkFormat bufferFormat,
248 VkDeviceSize writeBufferAccessRange,
249 bool accessOutOfBackingMemory);
250
~BufferWriteInstance(void)251 virtual ~BufferWriteInstance (void) {}
252 };
253
254 // RobustBufferAccessTest
255
256 const deUint32 RobustBufferAccessTest::s_testArraySize = 1024;
257 const deUint32 RobustBufferAccessTest::s_numberOfBytesAccessed = (deUint32)(16 * sizeof(float)); // size of mat4
258
RobustBufferAccessTest(tcu::TestContext & testContext,const std::string & name,const std::string & description,VkShaderStageFlags shaderStage,ShaderType shaderType,VkFormat bufferFormat)259 RobustBufferAccessTest::RobustBufferAccessTest (tcu::TestContext& testContext,
260 const std::string& name,
261 const std::string& description,
262 VkShaderStageFlags shaderStage,
263 ShaderType shaderType,
264 VkFormat bufferFormat)
265 : vkt::TestCase (testContext, name, description)
266 , m_shaderStage (shaderStage)
267 , m_shaderType (shaderType)
268 , m_bufferFormat (bufferFormat)
269 {
270 DE_ASSERT(m_shaderStage == VK_SHADER_STAGE_VERTEX_BIT || m_shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT || m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT);
271 }
272
checkSupport(Context & context) const273 void RobustBufferAccessTest::checkSupport(Context& context) const
274 {
275 if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") && !context.getDeviceFeatures().robustBufferAccess)
276 TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: robustBufferAccess not supported by this implementation");
277 }
278
genBufferShaderAccess(ShaderType shaderType,VkFormat bufferFormat,bool readFromStorage,std::ostringstream & bufferDefinition,std::ostringstream & bufferUse)279 void RobustBufferAccessTest::genBufferShaderAccess (ShaderType shaderType,
280 VkFormat bufferFormat,
281 bool readFromStorage,
282 std::ostringstream& bufferDefinition,
283 std::ostringstream& bufferUse)
284 {
285 if (isFloatFormat(bufferFormat))
286 {
287 bufferDefinition <<
288 "layout(binding = 0, " << (readFromStorage ? "std430" : "std140") << ") " << (readFromStorage ? "buffer" : "uniform") << " InBuffer\n"
289 "{\n"
290 " mat4 inMatrix[" << s_testArraySize << "];\n"
291 "};\n\n";
292
293 bufferDefinition <<
294 "layout(binding = 1, std430) buffer OutBuffer\n"
295 "{\n"
296 " mat4 outMatrix[" << s_testArraySize << "];\n"
297 "};\n\n";
298
299 bufferDefinition <<
300 "layout(binding = 2, std140) uniform Indices\n"
301 "{\n"
302 " int inIndex;\n"
303 " int outIndex;\n"
304 "};\n\n";
305
306 switch (shaderType)
307 {
308 case SHADER_TYPE_MATRIX_COPY:
309 bufferUse <<
310 " mat4 tmp = inMatrix[inIndex];\n"
311 " outMatrix[outIndex] = tmp;\n";
312 break;
313
314 case SHADER_TYPE_VECTOR_COPY:
315 bufferUse <<
316 " outMatrix[outIndex][0] = inMatrix[inIndex][0];\n"
317 " outMatrix[outIndex][1] = inMatrix[inIndex][1];\n"
318 " outMatrix[outIndex][2] = inMatrix[inIndex][2];\n"
319 " outMatrix[outIndex][3] = inMatrix[inIndex][3];\n";
320 break;
321
322 case SHADER_TYPE_SCALAR_COPY:
323 bufferUse <<
324 " outMatrix[outIndex][0][0] = inMatrix[inIndex][0][0];\n"
325 " outMatrix[outIndex][0][1] = inMatrix[inIndex][0][1];\n"
326 " outMatrix[outIndex][0][2] = inMatrix[inIndex][0][2];\n"
327 " outMatrix[outIndex][0][3] = inMatrix[inIndex][0][3];\n"
328
329 " outMatrix[outIndex][1][0] = inMatrix[inIndex][1][0];\n"
330 " outMatrix[outIndex][1][1] = inMatrix[inIndex][1][1];\n"
331 " outMatrix[outIndex][1][2] = inMatrix[inIndex][1][2];\n"
332 " outMatrix[outIndex][1][3] = inMatrix[inIndex][1][3];\n"
333
334 " outMatrix[outIndex][2][0] = inMatrix[inIndex][2][0];\n"
335 " outMatrix[outIndex][2][1] = inMatrix[inIndex][2][1];\n"
336 " outMatrix[outIndex][2][2] = inMatrix[inIndex][2][2];\n"
337 " outMatrix[outIndex][2][3] = inMatrix[inIndex][2][3];\n"
338
339 " outMatrix[outIndex][3][0] = inMatrix[inIndex][3][0];\n"
340 " outMatrix[outIndex][3][1] = inMatrix[inIndex][3][1];\n"
341 " outMatrix[outIndex][3][2] = inMatrix[inIndex][3][2];\n"
342 " outMatrix[outIndex][3][3] = inMatrix[inIndex][3][3];\n";
343 break;
344
345 default:
346 DE_ASSERT(false);
347 }
348 }
349 else
350 {
351 std::string typePrefixStr;
352
353 if (isUintFormat(bufferFormat))
354 {
355 typePrefixStr = "u";
356 }
357 else if (isIntFormat(bufferFormat))
358 {
359 typePrefixStr = "i";
360 }
361 else
362 {
363 DE_ASSERT(false);
364 }
365
366 typePrefixStr += (bufferFormat == vk::VK_FORMAT_R64_UINT || bufferFormat == vk::VK_FORMAT_R64_SINT) ?
367 "64" : "";
368
369 bufferDefinition <<
370 "layout(binding = 0, " << (readFromStorage ? "std430" : "std140") << ") " << (readFromStorage ? "buffer readonly" : "uniform") << " InBuffer\n"
371 "{\n"
372 " " << typePrefixStr << "vec4 inVecs[" << s_testArraySize << "][4];\n"
373 "};\n\n";
374
375 bufferDefinition <<
376 "layout(binding = 1, std430) buffer OutBuffer\n"
377 "{\n"
378 " " << typePrefixStr << "vec4 outVecs[" << s_testArraySize << "][4];\n"
379 "};\n\n";
380
381 bufferDefinition <<
382 "layout(binding = 2, std140) uniform Indices\n"
383 "{\n"
384 " int inIndex;\n"
385 " int outIndex;\n"
386 "};\n\n";
387
388 switch (shaderType)
389 {
390 case SHADER_TYPE_MATRIX_COPY:
391 // Shader type not supported for integer types.
392 DE_ASSERT(false);
393 break;
394
395 case SHADER_TYPE_VECTOR_COPY:
396 bufferUse <<
397 " outVecs[outIndex][0] = inVecs[inIndex][0];\n"
398 " outVecs[outIndex][1] = inVecs[inIndex][1];\n"
399 " outVecs[outIndex][2] = inVecs[inIndex][2];\n"
400 " outVecs[outIndex][3] = inVecs[inIndex][3];\n";
401 break;
402
403 case SHADER_TYPE_SCALAR_COPY:
404 bufferUse <<
405 " outVecs[outIndex][0][0] = inVecs[inIndex][0][0];\n"
406 " outVecs[outIndex][0][1] = inVecs[inIndex][0][1];\n"
407 " outVecs[outIndex][0][2] = inVecs[inIndex][0][2];\n"
408 " outVecs[outIndex][0][3] = inVecs[inIndex][0][3];\n"
409
410 " outVecs[outIndex][1][0] = inVecs[inIndex][1][0];\n"
411 " outVecs[outIndex][1][1] = inVecs[inIndex][1][1];\n"
412 " outVecs[outIndex][1][2] = inVecs[inIndex][1][2];\n"
413 " outVecs[outIndex][1][3] = inVecs[inIndex][1][3];\n"
414
415 " outVecs[outIndex][2][0] = inVecs[inIndex][2][0];\n"
416 " outVecs[outIndex][2][1] = inVecs[inIndex][2][1];\n"
417 " outVecs[outIndex][2][2] = inVecs[inIndex][2][2];\n"
418 " outVecs[outIndex][2][3] = inVecs[inIndex][2][3];\n"
419
420 " outVecs[outIndex][3][0] = inVecs[inIndex][3][0];\n"
421 " outVecs[outIndex][3][1] = inVecs[inIndex][3][1];\n"
422 " outVecs[outIndex][3][2] = inVecs[inIndex][3][2];\n"
423 " outVecs[outIndex][3][3] = inVecs[inIndex][3][3];\n";
424 break;
425
426 default:
427 DE_ASSERT(false);
428 }
429 }
430 }
431
genTexelBufferShaderAccess(VkFormat bufferFormat,std::ostringstream & bufferDefinition,std::ostringstream & bufferUse,bool readFromStorage)432 void RobustBufferAccessTest::genTexelBufferShaderAccess (VkFormat bufferFormat,
433 std::ostringstream& bufferDefinition,
434 std::ostringstream& bufferUse,
435 bool readFromStorage)
436 {
437 const char* layoutTypeStr;
438 const char* inTexelBufferTypeStr;
439 const char* outTexelBufferTypeStr;
440 const deUint32 texelSize = mapVkFormat(bufferFormat).getPixelSize();
441
442 if (isFloatFormat(bufferFormat))
443 {
444 layoutTypeStr = "rgba32f";
445 inTexelBufferTypeStr = readFromStorage ? "imageBuffer" : "textureBuffer";
446 outTexelBufferTypeStr = "imageBuffer";
447 }
448 else if (isUintFormat(bufferFormat))
449 {
450 layoutTypeStr = "rgba32ui";
451 inTexelBufferTypeStr = readFromStorage ? "uimageBuffer" : "utextureBuffer";
452 outTexelBufferTypeStr = "uimageBuffer";
453 }
454 else if (isIntFormat(bufferFormat))
455 {
456 layoutTypeStr = "rgba32i";
457 inTexelBufferTypeStr = readFromStorage ? "iimageBuffer" : "itextureBuffer";
458 outTexelBufferTypeStr = "iimageBuffer";
459 }
460 else if (bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32)
461 {
462 layoutTypeStr = "rgb10_a2";
463 inTexelBufferTypeStr = readFromStorage ? "imageBuffer" : "textureBuffer"; outTexelBufferTypeStr = "imageBuffer";
464 }
465 else
466 {
467 TCU_THROW(NotSupportedError, (std::string("Unsupported format: ") + getFormatName(bufferFormat)).c_str());
468 }
469
470 bufferDefinition << "layout(set = 0, binding = 0" << ((readFromStorage) ? (std::string(", ") + layoutTypeStr) : "") << ") uniform highp "
471 << ((readFromStorage) ? "readonly " : "") << inTexelBufferTypeStr << " inImage;\n";
472
473 bufferDefinition << "layout(set = 0, binding = 1, " << layoutTypeStr << ") uniform highp writeonly " << outTexelBufferTypeStr << " outImage;\n";
474
475 bufferDefinition <<
476 "layout(binding = 2, std140) uniform Offsets\n"
477 "{\n"
478 " int inOffset;\n"
479 " int outOffset;\n"
480 "};\n\n";
481
482 bufferUse << " for (int i = 0; i < " << (s_numberOfBytesAccessed / texelSize) << "; i++)\n"
483 << " {\n"
484 << " imageStore(outImage, outOffset + i, " << (readFromStorage ? "imageLoad" : "texelFetch") << "(inImage, inOffset + i));\n"
485 << " }\n";
486 }
487
initBufferAccessPrograms(SourceCollections & programCollection,VkShaderStageFlags shaderStage,ShaderType shaderType,VkFormat bufferFormat,bool readFromStorage)488 void RobustBufferAccessTest::initBufferAccessPrograms (SourceCollections& programCollection,
489 VkShaderStageFlags shaderStage,
490 ShaderType shaderType,
491 VkFormat bufferFormat,
492 bool readFromStorage)
493 {
494 std::ostringstream bufferDefinition;
495 std::ostringstream bufferUse;
496 std::string extensions;
497
498 if (bufferFormat == vk::VK_FORMAT_R64_UINT || bufferFormat == vk::VK_FORMAT_R64_SINT)
499 {
500 extensions = "#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require\n";
501 }
502
503 if (shaderType != SHADER_TYPE_TEXEL_COPY)
504 {
505 genBufferShaderAccess(shaderType, bufferFormat, readFromStorage, bufferDefinition, bufferUse);
506 }
507
508 if (shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
509 {
510 std::ostringstream computeShaderSource;
511
512 if (shaderType == SHADER_TYPE_TEXEL_COPY)
513 genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage);
514
515 computeShaderSource <<
516 "#version 440\n"
517 "#extension GL_EXT_texture_buffer : require\n"
518 << extensions <<
519 "precision highp float;\n"
520 "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
521 << bufferDefinition.str() <<
522 "void main (void)\n"
523 "{\n"
524 << bufferUse.str() <<
525 "}\n";
526
527 programCollection.glslSources.add("compute") << glu::ComputeSource(computeShaderSource.str());
528 }
529 else
530 {
531 std::ostringstream vertexShaderSource;
532 std::ostringstream fragmentShaderSource;
533
534 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
535 {
536 if (shaderType == SHADER_TYPE_TEXEL_COPY)
537 genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage);
538
539 vertexShaderSource <<
540 "#version 440\n"
541 "#extension GL_EXT_texture_buffer : require\n"
542 << extensions <<
543 "precision highp float;\n"
544 "layout(location = 0) in vec4 position;\n\n"
545 << bufferDefinition.str() << "\n"
546 "out gl_PerVertex {\n"
547 " vec4 gl_Position;\n"
548 "};\n\n"
549 "void main (void)\n"
550 "{\n"
551 << bufferUse.str() <<
552 " gl_Position = position;\n"
553 "}\n";
554 }
555 else
556 {
557 vertexShaderSource <<
558 "#version 440\n"
559 "precision highp float;\n"
560 "layout(location = 0) in vec4 position;\n\n"
561 "out gl_PerVertex {\n"
562 " vec4 gl_Position;\n"
563 "};\n\n"
564 "void main (void)\n"
565 "{\n"
566 " gl_Position = position;\n"
567 "}\n";
568 }
569
570 programCollection.glslSources.add("vertex") << glu::VertexSource(vertexShaderSource.str());
571
572 if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
573 {
574 if (shaderType == SHADER_TYPE_TEXEL_COPY)
575 genTexelBufferShaderAccess(bufferFormat, bufferDefinition, bufferUse, readFromStorage);
576
577 fragmentShaderSource <<
578 "#version 440\n"
579 "#extension GL_EXT_texture_buffer : require\n"
580 << extensions <<
581 "precision highp float;\n"
582 "layout(location = 0) out vec4 fragColor;\n"
583 << bufferDefinition.str() <<
584 "void main (void)\n"
585 "{\n"
586 << bufferUse.str() <<
587 " fragColor = vec4(1.0);\n"
588 "}\n";
589 }
590 else
591 {
592 fragmentShaderSource <<
593 "#version 440\n"
594 "precision highp float;\n"
595 "layout(location = 0) out vec4 fragColor;\n\n"
596 "void main (void)\n"
597 "{\n"
598 " fragColor = vec4(1.0);\n"
599 "}\n";
600 }
601
602 programCollection.glslSources.add("fragment") << glu::FragmentSource(fragmentShaderSource.str());
603 }
604 }
605
606 // RobustBufferReadTest
607
RobustBufferReadTest(tcu::TestContext & testContext,const std::string & name,const std::string & description,VkShaderStageFlags shaderStage,ShaderType shaderType,VkFormat bufferFormat,VkDeviceSize readAccessRange,bool readFromStorage,bool accessOutOfBackingMemory)608 RobustBufferReadTest::RobustBufferReadTest (tcu::TestContext& testContext,
609 const std::string& name,
610 const std::string& description,
611 VkShaderStageFlags shaderStage,
612 ShaderType shaderType,
613 VkFormat bufferFormat,
614 VkDeviceSize readAccessRange,
615 bool readFromStorage,
616 bool accessOutOfBackingMemory)
617 : RobustBufferAccessTest (testContext, name, description, shaderStage, shaderType, bufferFormat)
618 , m_readFromStorage (readFromStorage)
619 , m_readAccessRange (readAccessRange)
620 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
621 {
622 }
623
initPrograms(SourceCollections & programCollection) const624 void RobustBufferReadTest::initPrograms (SourceCollections& programCollection) const
625 {
626 initBufferAccessPrograms(programCollection, m_shaderStage, m_shaderType, m_bufferFormat, m_readFromStorage);
627 }
628
createInstance(Context & context) const629 TestInstance* RobustBufferReadTest::createInstance (Context& context) const
630 {
631 Move<VkDevice> device = createRobustBufferAccessDevice(context);
632
633 return new BufferReadInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_readFromStorage, m_readAccessRange, m_accessOutOfBackingMemory);
634 }
635
636 // RobustBufferWriteTest
637
RobustBufferWriteTest(tcu::TestContext & testContext,const std::string & name,const std::string & description,VkShaderStageFlags shaderStage,ShaderType shaderType,VkFormat bufferFormat,VkDeviceSize writeAccessRange,bool accessOutOfBackingMemory)638 RobustBufferWriteTest::RobustBufferWriteTest (tcu::TestContext& testContext,
639 const std::string& name,
640 const std::string& description,
641 VkShaderStageFlags shaderStage,
642 ShaderType shaderType,
643 VkFormat bufferFormat,
644 VkDeviceSize writeAccessRange,
645 bool accessOutOfBackingMemory)
646
647 : RobustBufferAccessTest (testContext, name, description, shaderStage, shaderType, bufferFormat)
648 , m_writeAccessRange (writeAccessRange)
649 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
650 {
651 }
652
initPrograms(SourceCollections & programCollection) const653 void RobustBufferWriteTest::initPrograms (SourceCollections& programCollection) const
654 {
655 initBufferAccessPrograms(programCollection, m_shaderStage, m_shaderType, m_bufferFormat, false /* readFromStorage */);
656 }
657
createInstance(Context & context) const658 TestInstance* RobustBufferWriteTest::createInstance (Context& context) const
659 {
660 Move<VkDevice> device = createRobustBufferAccessDevice(context);
661
662 return new BufferWriteInstance(context, device, m_shaderType, m_shaderStage, m_bufferFormat, m_writeAccessRange, m_accessOutOfBackingMemory);
663 }
664
665 // BufferAccessInstance
666
BufferAccessInstance(Context & context,Move<VkDevice> device,ShaderType shaderType,VkShaderStageFlags shaderStage,VkFormat bufferFormat,BufferAccessType bufferAccessType,VkDeviceSize inBufferAccessRange,VkDeviceSize outBufferAccessRange,bool accessOutOfBackingMemory)667 BufferAccessInstance::BufferAccessInstance (Context& context,
668 Move<VkDevice> device,
669 ShaderType shaderType,
670 VkShaderStageFlags shaderStage,
671 VkFormat bufferFormat,
672 BufferAccessType bufferAccessType,
673 VkDeviceSize inBufferAccessRange,
674 VkDeviceSize outBufferAccessRange,
675 bool accessOutOfBackingMemory)
676 : vkt::TestInstance (context)
677 , m_device (device)
678 , m_shaderType (shaderType)
679 , m_shaderStage (shaderStage)
680 , m_bufferFormat (bufferFormat)
681 , m_bufferAccessType (bufferAccessType)
682 , m_inBufferAccessRange (inBufferAccessRange)
683 , m_outBufferAccessRange (outBufferAccessRange)
684 , m_accessOutOfBackingMemory (accessOutOfBackingMemory)
685 {
686 const DeviceInterface& vk = context.getDeviceInterface();
687 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
688 const bool isTexelAccess = !!(m_shaderType == SHADER_TYPE_TEXEL_COPY);
689 const bool readFromStorage = !!(m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE);
690 SimpleAllocator memAlloc (vk, *m_device, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
691 tcu::TestLog& log = m_context.getTestContext().getLog();
692
693 DE_ASSERT(RobustBufferAccessTest::s_numberOfBytesAccessed % sizeof(deUint32) == 0);
694 DE_ASSERT(inBufferAccessRange <= RobustBufferAccessTest::s_numberOfBytesAccessed);
695 DE_ASSERT(outBufferAccessRange <= RobustBufferAccessTest::s_numberOfBytesAccessed);
696
697 if (m_bufferFormat == VK_FORMAT_R64_UINT || m_bufferFormat == VK_FORMAT_R64_SINT)
698 {
699 context.requireDeviceFunctionality("VK_EXT_shader_image_atomic_int64");
700 }
701
702 // Check storage support
703 if (shaderStage == VK_SHADER_STAGE_VERTEX_BIT)
704 {
705 if (!context.getDeviceFeatures().vertexPipelineStoresAndAtomics)
706 {
707 TCU_THROW(NotSupportedError, "Stores not supported in vertex stage");
708 }
709 }
710 else if (shaderStage == VK_SHADER_STAGE_FRAGMENT_BIT)
711 {
712 if (!context.getDeviceFeatures().fragmentStoresAndAtomics)
713 {
714 TCU_THROW(NotSupportedError, "Stores not supported in fragment stage");
715 }
716 }
717
718 // Check format support
719 {
720 VkFormatFeatureFlags requiredFormatFeatures = 0;
721 const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(), context.getPhysicalDevice(), m_bufferFormat);
722
723 if (isTexelAccess)
724 {
725 requiredFormatFeatures = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT;
726 }
727
728 if ((formatProperties.bufferFeatures & requiredFormatFeatures) != requiredFormatFeatures)
729 {
730 TCU_THROW(NotSupportedError, (std::string("Format cannot be used in uniform and storage") + (isTexelAccess ? " texel" : "") + " buffers: "
731 + getFormatName(m_bufferFormat)).c_str());
732 }
733 }
734
735 // Create buffer to read data from
736 {
737 VkBufferUsageFlags inBufferUsageFlags;
738 VkMemoryRequirements inBufferMemoryReqs;
739
740 if (isTexelAccess)
741 {
742 inBufferUsageFlags = readFromStorage ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
743 }
744 else
745 {
746 inBufferUsageFlags = readFromStorage ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
747 }
748
749 const VkBufferCreateInfo inBufferParams =
750 {
751 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
752 DE_NULL, // const void* pNext;
753 0u, // VkBufferCreateFlags flags;
754 m_inBufferAccessRange, // VkDeviceSize size;
755 inBufferUsageFlags, // VkBufferUsageFlags usage;
756 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
757 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount;
758 DE_NULL // const deUint32* pQueueFamilyIndices;
759 };
760
761 m_inBuffer = createBuffer(vk, *m_device, &inBufferParams);
762
763 inBufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_inBuffer);
764 m_inBufferAllocSize = inBufferMemoryReqs.size;
765 m_inBufferAlloc = memAlloc.allocate(inBufferMemoryReqs, MemoryRequirement::HostVisible);
766
767 // Size of the most restrictive bound
768 m_inBufferMaxAccessRange = min(m_inBufferAllocSize, min(inBufferParams.size, m_inBufferAccessRange));
769
770 VK_CHECK(vk.bindBufferMemory(*m_device, *m_inBuffer, m_inBufferAlloc->getMemory(), m_inBufferAlloc->getOffset()));
771 populateBufferWithTestValues(m_inBufferAlloc->getHostPtr(), m_inBufferAllocSize, m_bufferFormat);
772 flushMappedMemoryRange(vk, *m_device, m_inBufferAlloc->getMemory(), m_inBufferAlloc->getOffset(), VK_WHOLE_SIZE);
773
774 log << tcu::TestLog::Message << "inBufferAllocSize = " << m_inBufferAllocSize << tcu::TestLog::EndMessage;
775 log << tcu::TestLog::Message << "inBufferMaxAccessRange = " << m_inBufferMaxAccessRange << tcu::TestLog::EndMessage;
776 }
777
778 // Create buffer to write data into
779 {
780 VkMemoryRequirements outBufferMemoryReqs;
781 const VkBufferUsageFlags outBufferUsageFlags = (m_shaderType == SHADER_TYPE_TEXEL_COPY) ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT
782 : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
783
784 const VkBufferCreateInfo outBufferParams =
785 {
786 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
787 DE_NULL, // const void* pNext;
788 0u, // VkBufferCreateFlags flags;
789 m_outBufferAccessRange, // VkDeviceSize size;
790 outBufferUsageFlags, // VkBufferUsageFlags usage;
791 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
792 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount;
793 DE_NULL // const deUint32* pQueueFamilyIndices;
794 };
795
796 m_outBuffer = createBuffer(vk, *m_device, &outBufferParams);
797
798 outBufferMemoryReqs = getBufferMemoryRequirements(vk, *m_device, *m_outBuffer);
799 m_outBufferAllocSize = outBufferMemoryReqs.size;
800 m_outBufferAlloc = memAlloc.allocate(outBufferMemoryReqs, MemoryRequirement::HostVisible);
801
802 // If we are requesting access out of the memory that backs the buffer, make sure the test is able to do so.
803 if (m_accessOutOfBackingMemory)
804 {
805 if (m_outBufferAllocSize >= ((RobustBufferAccessTest::s_testArraySize + 1) * RobustBufferAccessTest::s_numberOfBytesAccessed))
806 {
807 TCU_THROW(NotSupportedError, "Cannot access beyond the end of the memory that backs the buffer");
808 }
809 }
810
811 // Size of the most restrictive bound
812 m_outBufferMaxAccessRange = min(m_outBufferAllocSize, min(outBufferParams.size, m_outBufferAccessRange));
813
814 VK_CHECK(vk.bindBufferMemory(*m_device, *m_outBuffer, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset()));
815 deMemset(m_outBufferAlloc->getHostPtr(), 0xFF, (size_t)m_outBufferAllocSize);
816 flushMappedMemoryRange(vk, *m_device, m_outBufferAlloc->getMemory(), m_outBufferAlloc->getOffset(), VK_WHOLE_SIZE);
817
818 log << tcu::TestLog::Message << "outBufferAllocSize = " << m_outBufferAllocSize << tcu::TestLog::EndMessage;
819 log << tcu::TestLog::Message << "outBufferMaxAccessRange = " << m_outBufferMaxAccessRange << tcu::TestLog::EndMessage;
820 }
821
822 // Create buffer for indices/offsets
823 {
824 struct IndicesBuffer
825 {
826 deInt32 inIndex;
827 deInt32 outIndex;
828 };
829
830 IndicesBuffer indices = { 0, 0 };
831
832 const VkBufferCreateInfo indicesBufferParams =
833 {
834 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
835 DE_NULL, // const void* pNext;
836 0u, // VkBufferCreateFlags flags;
837 sizeof(IndicesBuffer), // VkDeviceSize size;
838 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, // VkBufferUsageFlags usage;
839 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
840 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount;
841 DE_NULL, // const deUint32* pQueueFamilyIndices;
842 };
843
844 m_indicesBuffer = createBuffer(vk, *m_device, &indicesBufferParams);
845 m_indicesBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_indicesBuffer), MemoryRequirement::HostVisible);
846
847 VK_CHECK(vk.bindBufferMemory(*m_device, *m_indicesBuffer, m_indicesBufferAlloc->getMemory(), m_indicesBufferAlloc->getOffset()));
848
849 if (m_accessOutOfBackingMemory)
850 {
851 if (m_bufferAccessType == BUFFER_ACCESS_TYPE_WRITE)
852 {
853 indices.outIndex = RobustBufferAccessTest::s_testArraySize - 1;
854 }
855 else
856 {
857 indices.inIndex = RobustBufferAccessTest::s_testArraySize - 1;
858 }
859 }
860
861 deMemcpy(m_indicesBufferAlloc->getHostPtr(), &indices, sizeof(IndicesBuffer));
862
863 flushMappedMemoryRange(vk, *m_device, m_indicesBufferAlloc->getMemory(), m_indicesBufferAlloc->getOffset(), VK_WHOLE_SIZE);
864
865 log << tcu::TestLog::Message << "inIndex = " << indices.inIndex << tcu::TestLog::EndMessage;
866 log << tcu::TestLog::Message << "outIndex = " << indices.outIndex << tcu::TestLog::EndMessage;
867 }
868
869 // Create descriptor data
870 {
871 VkDescriptorType inBufferDescriptorType;
872 VkDescriptorType outBufferDescriptorType;
873
874 if (isTexelAccess)
875 {
876 inBufferDescriptorType = readFromStorage ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
877 outBufferDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
878 }
879 else
880 {
881 inBufferDescriptorType = readFromStorage ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
882 outBufferDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
883 }
884
885 DescriptorPoolBuilder descriptorPoolBuilder;
886 descriptorPoolBuilder.addType(inBufferDescriptorType, 1u);
887 descriptorPoolBuilder.addType(outBufferDescriptorType, 1u);
888 descriptorPoolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u);
889 m_descriptorPool = descriptorPoolBuilder.build(vk, *m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
890
891 DescriptorSetLayoutBuilder setLayoutBuilder;
892 setLayoutBuilder.addSingleBinding(inBufferDescriptorType, VK_SHADER_STAGE_ALL);
893 setLayoutBuilder.addSingleBinding(outBufferDescriptorType, VK_SHADER_STAGE_ALL);
894 setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_ALL);
895 m_descriptorSetLayout = setLayoutBuilder.build(vk, *m_device);
896
897 const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo =
898 {
899 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
900 DE_NULL, // const void* pNext;
901 *m_descriptorPool, // VkDescriptorPool descriptorPool;
902 1u, // deUint32 setLayoutCount;
903 &m_descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts;
904 };
905
906 m_descriptorSet = allocateDescriptorSet(vk, *m_device, &descriptorSetAllocateInfo);
907
908 DescriptorSetUpdateBuilder setUpdateBuilder;
909
910 if (isTexelAccess)
911 {
912 const VkBufferViewCreateInfo inBufferViewCreateInfo =
913 {
914 VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType;
915 DE_NULL, // const void* pNext;
916 0u, // VkBufferViewCreateFlags flags;
917 *m_inBuffer, // VkBuffer buffer;
918 m_bufferFormat, // VkFormat format;
919 0ull, // VkDeviceSize offset;
920 m_inBufferAccessRange // VkDeviceSize range;
921 };
922 m_inTexelBufferView = createBufferView(vk, *m_device, &inBufferViewCreateInfo, DE_NULL);
923
924 const VkBufferViewCreateInfo outBufferViewCreateInfo =
925 {
926 VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType;
927 DE_NULL, // const void* pNext;
928 0u, // VkBufferViewCreateFlags flags;
929 *m_outBuffer, // VkBuffer buffer;
930 m_bufferFormat, // VkFormat format;
931 0ull, // VkDeviceSize offset;
932 m_outBufferAccessRange, // VkDeviceSize range;
933 };
934 m_outTexelBufferView = createBufferView(vk, *m_device, &outBufferViewCreateInfo, DE_NULL);
935
936 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), inBufferDescriptorType, &m_inTexelBufferView.get());
937 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), outBufferDescriptorType, &m_outTexelBufferView.get());
938 }
939 else
940 {
941 const VkDescriptorBufferInfo inBufferDescriptorInfo = makeDescriptorBufferInfo(*m_inBuffer, 0ull, m_inBufferAccessRange);
942 const VkDescriptorBufferInfo outBufferDescriptorInfo = makeDescriptorBufferInfo(*m_outBuffer, 0ull, m_outBufferAccessRange);
943
944 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0), inBufferDescriptorType, &inBufferDescriptorInfo);
945 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1), outBufferDescriptorType, &outBufferDescriptorInfo);
946 }
947
948 const VkDescriptorBufferInfo indicesBufferDescriptorInfo = makeDescriptorBufferInfo(*m_indicesBuffer, 0ull, 8ull);
949 setUpdateBuilder.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &indicesBufferDescriptorInfo);
950
951 setUpdateBuilder.update(vk, *m_device);
952 }
953
954 // Create fence
955 {
956 const VkFenceCreateInfo fenceParams =
957 {
958 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
959 DE_NULL, // const void* pNext;
960 0u // VkFenceCreateFlags flags;
961 };
962
963 m_fence = createFence(vk, *m_device, &fenceParams);
964 }
965
966 // Get queue
967 vk.getDeviceQueue(*m_device, queueFamilyIndex, 0, &m_queue);
968
969 if (m_shaderStage == VK_SHADER_STAGE_COMPUTE_BIT)
970 {
971 m_testEnvironment = de::MovePtr<TestEnvironment>(new ComputeEnvironment(m_context, *m_device, *m_descriptorSetLayout, *m_descriptorSet));
972 }
973 else
974 {
975 using tcu::Vec4;
976
977 const VkVertexInputBindingDescription vertexInputBindingDescription =
978 {
979 0u, // deUint32 binding;
980 sizeof(tcu::Vec4), // deUint32 strideInBytes;
981 VK_VERTEX_INPUT_RATE_VERTEX // VkVertexInputStepRate inputRate;
982 };
983
984 const VkVertexInputAttributeDescription vertexInputAttributeDescription =
985 {
986 0u, // deUint32 location;
987 0u, // deUint32 binding;
988 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
989 0u // deUint32 offset;
990 };
991
992 const Vec4 vertices[] =
993 {
994 Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
995 Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
996 Vec4(1.0f, -1.0f, 0.0f, 1.0f),
997 };
998
999 // Create vertex buffer
1000 {
1001 const VkDeviceSize vertexBufferSize = (VkDeviceSize)(4u * sizeof(tcu::Vec4));
1002 const VkBufferCreateInfo vertexBufferParams =
1003 {
1004 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
1005 DE_NULL, // const void* pNext;
1006 0u, // VkBufferCreateFlags flags;
1007 vertexBufferSize, // VkDeviceSize size;
1008 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
1009 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1010 VK_QUEUE_FAMILY_IGNORED, // deUint32 queueFamilyIndexCount;
1011 DE_NULL // const deUint32* pQueueFamilyIndices;
1012 };
1013
1014 DE_ASSERT(vertexBufferSize > 0);
1015
1016 m_vertexBuffer = createBuffer(vk, *m_device, &vertexBufferParams);
1017 m_vertexBufferAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, *m_device, *m_vertexBuffer), MemoryRequirement::HostVisible);
1018
1019 VK_CHECK(vk.bindBufferMemory(*m_device, *m_vertexBuffer, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset()));
1020
1021 // Load vertices into vertex buffer
1022 deMemcpy(m_vertexBufferAlloc->getHostPtr(), vertices, sizeof(tcu::Vec4) * DE_LENGTH_OF_ARRAY(vertices));
1023 flushMappedMemoryRange(vk, *m_device, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset(), VK_WHOLE_SIZE);
1024 }
1025
1026 const GraphicsEnvironment::DrawConfig drawWithOneVertexBuffer =
1027 {
1028 std::vector<VkBuffer>(1, *m_vertexBuffer), // std::vector<VkBuffer> vertexBuffers;
1029 DE_LENGTH_OF_ARRAY(vertices), // deUint32 vertexCount;
1030 1, // deUint32 instanceCount;
1031 DE_NULL, // VkBuffer indexBuffer;
1032 0u, // deUint32 indexCount;
1033 };
1034
1035 m_testEnvironment = de::MovePtr<TestEnvironment>(new GraphicsEnvironment(m_context,
1036 *m_device,
1037 *m_descriptorSetLayout,
1038 *m_descriptorSet,
1039 GraphicsEnvironment::VertexBindings(1, vertexInputBindingDescription),
1040 GraphicsEnvironment::VertexAttributes(1, vertexInputAttributeDescription),
1041 drawWithOneVertexBuffer));
1042 }
1043 }
1044
1045 // Verifies if the buffer has the value initialized by BufferAccessInstance::populateReadBuffer at a given offset.
isExpectedValueFromInBuffer(VkDeviceSize offsetInBytes,const void * valuePtr,VkDeviceSize valueSize)1046 bool BufferAccessInstance::isExpectedValueFromInBuffer (VkDeviceSize offsetInBytes, const void* valuePtr, VkDeviceSize valueSize)
1047 {
1048 DE_ASSERT(offsetInBytes % 4 == 0);
1049 DE_ASSERT(offsetInBytes < m_inBufferAllocSize);
1050
1051 const deUint32 valueIndex = deUint32(offsetInBytes / 4) + 2;
1052
1053 if (isUintFormat(m_bufferFormat))
1054 {
1055 return !deMemCmp(valuePtr, &valueIndex, (size_t)valueSize);
1056 }
1057 else if (isIntFormat(m_bufferFormat))
1058 {
1059 const deInt32 value = -deInt32(valueIndex);
1060 return !deMemCmp(valuePtr, &value, (size_t)valueSize);
1061 }
1062 else if (isFloatFormat(m_bufferFormat))
1063 {
1064 const float value = float(valueIndex);
1065 return !deMemCmp(valuePtr, &value, (size_t)valueSize);
1066 }
1067 else if (m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32)
1068 {
1069 const deUint32 r = ((valueIndex + 0) & ((2u << 10) - 1u));
1070 const deUint32 g = ((valueIndex + 1) & ((2u << 10) - 1u));
1071 const deUint32 b = ((valueIndex + 2) & ((2u << 10) - 1u));
1072 const deUint32 a = ((valueIndex + 0) & ((2u << 2) - 1u));
1073 const deUint32 abgr = (a << 30) | (b << 20) | (g << 10) | r;
1074
1075 return !deMemCmp(valuePtr, &abgr, (size_t)valueSize);
1076 }
1077 else
1078 {
1079 DE_ASSERT(false);
1080 return false;
1081 }
1082 }
1083
isOutBufferValueUnchanged(VkDeviceSize offsetInBytes,VkDeviceSize valueSize)1084 bool BufferAccessInstance::isOutBufferValueUnchanged (VkDeviceSize offsetInBytes, VkDeviceSize valueSize)
1085 {
1086 const deUint8 *const outValuePtr = (deUint8*)m_outBufferAlloc->getHostPtr() + offsetInBytes;
1087 const deUint32 defaultValue = 0xFFFFFFFFu;
1088
1089 return !deMemCmp(outValuePtr, &defaultValue, (size_t)valueSize);
1090 }
1091
iterate(void)1092 tcu::TestStatus BufferAccessInstance::iterate (void)
1093 {
1094 const DeviceInterface& vk = m_context.getDeviceInterface();
1095 const vk::VkCommandBuffer cmdBuffer = m_testEnvironment->getCommandBuffer();
1096
1097 // Submit command buffer
1098 {
1099 const VkSubmitInfo submitInfo =
1100 {
1101 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
1102 DE_NULL, // const void* pNext;
1103 0u, // deUint32 waitSemaphoreCount;
1104 DE_NULL, // const VkSemaphore* pWaitSemaphores;
1105 DE_NULL, // const VkPIpelineStageFlags* pWaitDstStageMask;
1106 1u, // deUint32 commandBufferCount;
1107 &cmdBuffer, // const VkCommandBuffer* pCommandBuffers;
1108 0u, // deUint32 signalSemaphoreCount;
1109 DE_NULL // const VkSemaphore* pSignalSemaphores;
1110 };
1111
1112 VK_CHECK(vk.resetFences(*m_device, 1, &m_fence.get()));
1113 VK_CHECK(vk.queueSubmit(m_queue, 1, &submitInfo, *m_fence));
1114 VK_CHECK(vk.waitForFences(*m_device, 1, &m_fence.get(), true, ~(0ull) /* infinity */));
1115 }
1116
1117 // Prepare result buffer for read
1118 {
1119 const VkMappedMemoryRange outBufferRange =
1120 {
1121 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType;
1122 DE_NULL, // const void* pNext;
1123 m_outBufferAlloc->getMemory(), // VkDeviceMemory mem;
1124 0ull, // VkDeviceSize offset;
1125 m_outBufferAllocSize, // VkDeviceSize size;
1126 };
1127
1128 VK_CHECK(vk.invalidateMappedMemoryRanges(*m_device, 1u, &outBufferRange));
1129 }
1130
1131 if (verifyResult())
1132 return tcu::TestStatus::pass("All values OK");
1133 else
1134 return tcu::TestStatus::fail("Invalid value(s) found");
1135 }
1136
verifyResult(void)1137 bool BufferAccessInstance::verifyResult (void)
1138 {
1139 std::ostringstream logMsg;
1140 tcu::TestLog& log = m_context.getTestContext().getLog();
1141 const bool isReadAccess = !!(m_bufferAccessType == BUFFER_ACCESS_TYPE_READ || m_bufferAccessType == BUFFER_ACCESS_TYPE_READ_FROM_STORAGE);
1142 const void* inDataPtr = m_inBufferAlloc->getHostPtr();
1143 const void* outDataPtr = m_outBufferAlloc->getHostPtr();
1144 bool allOk = true;
1145 deUint32 valueNdx = 0;
1146 const VkDeviceSize maxAccessRange = isReadAccess ? m_inBufferMaxAccessRange : m_outBufferMaxAccessRange;
1147
1148 for (VkDeviceSize offsetInBytes = 0; offsetInBytes < m_outBufferAllocSize; offsetInBytes += 4)
1149 {
1150 deUint8* outValuePtr = (deUint8*)outDataPtr + offsetInBytes;
1151 const size_t outValueSize = (size_t)min(4, (m_outBufferAllocSize - offsetInBytes));
1152
1153 if (offsetInBytes >= RobustBufferAccessTest::s_numberOfBytesAccessed)
1154 {
1155 // The shader will only write 16 values into the result buffer. The rest of the values
1156 // should remain unchanged or may be modified if we are writing out of bounds.
1157 if (!isOutBufferValueUnchanged(offsetInBytes, outValueSize)
1158 && (isReadAccess || !isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, 4)))
1159 {
1160 logMsg << "\nValue " << valueNdx++ << " has been modified with an unknown value: " << *((deUint32 *)outValuePtr);
1161 allOk = false;
1162 }
1163 }
1164 else
1165 {
1166 const deInt32 distanceToOutOfBounds = (deInt32)maxAccessRange - (deInt32)offsetInBytes;
1167 bool isOutOfBoundsAccess = false;
1168
1169 logMsg << "\n" << valueNdx++ << ": ";
1170
1171 logValue(logMsg, outValuePtr, m_bufferFormat, outValueSize);
1172
1173 if (m_accessOutOfBackingMemory)
1174 {
1175 isOutOfBoundsAccess = true;
1176 }
1177 else
1178 {
1179 // Check if the shader operation accessed an operand located less than 16 bytes away
1180 // from the out of bounds address.
1181
1182 deUint32 operandSize = 0;
1183
1184 switch (m_shaderType)
1185 {
1186 case SHADER_TYPE_SCALAR_COPY:
1187 operandSize = 4; // Size of scalar
1188 break;
1189
1190 case SHADER_TYPE_VECTOR_COPY:
1191 operandSize = 4 * ((m_bufferFormat == vk::VK_FORMAT_R64_UINT || m_bufferFormat == vk::VK_FORMAT_R64_SINT) ? 8 : 4);// Size of vec4
1192 break;
1193
1194 case SHADER_TYPE_MATRIX_COPY:
1195 operandSize = 4 * 16; // Size of mat4
1196 break;
1197
1198 case SHADER_TYPE_TEXEL_COPY:
1199 operandSize = mapVkFormat(m_bufferFormat).getPixelSize();
1200 break;
1201
1202 default:
1203 DE_ASSERT(false);
1204 }
1205
1206 isOutOfBoundsAccess = (maxAccessRange < 16)
1207 || (((offsetInBytes / operandSize + 1) * operandSize) > (maxAccessRange - 16));
1208 }
1209
1210 if (isOutOfBoundsAccess)
1211 {
1212 logMsg << " (out of bounds " << (isReadAccess ? "read": "write") << ")";
1213
1214 const bool isValuePartiallyOutOfBounds = ((distanceToOutOfBounds > 0) && ((deUint32)distanceToOutOfBounds < 4));
1215 bool isValidValue = false;
1216
1217 if (isValuePartiallyOutOfBounds && !m_accessOutOfBackingMemory)
1218 {
1219 // The value is partially out of bounds
1220
1221 bool isOutOfBoundsPartOk = true;
1222 bool isWithinBoundsPartOk = true;
1223
1224 if (isReadAccess)
1225 {
1226 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, distanceToOutOfBounds);
1227 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, (deUint8*)outValuePtr + distanceToOutOfBounds , outValueSize - distanceToOutOfBounds);
1228 }
1229 else
1230 {
1231 isWithinBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, distanceToOutOfBounds)
1232 || isOutBufferValueUnchanged(offsetInBytes, distanceToOutOfBounds);
1233
1234 isOutOfBoundsPartOk = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, (deUint8*)outValuePtr + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds)
1235 || isOutBufferValueUnchanged(offsetInBytes + distanceToOutOfBounds, outValueSize - distanceToOutOfBounds);
1236 }
1237
1238 logMsg << ", first " << distanceToOutOfBounds << " byte(s) " << (isWithinBoundsPartOk ? "OK": "wrong");
1239 logMsg << ", last " << outValueSize - distanceToOutOfBounds << " byte(s) " << (isOutOfBoundsPartOk ? "OK": "wrong");
1240
1241 isValidValue = isWithinBoundsPartOk && isOutOfBoundsPartOk;
1242 }
1243 else
1244 {
1245 if (isReadAccess)
1246 {
1247 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, outValueSize);
1248 }
1249 else
1250 {
1251 isValidValue = isOutBufferValueUnchanged(offsetInBytes, outValueSize);
1252
1253 if (!isValidValue)
1254 {
1255 // Out of bounds writes may modify values withing the memory ranges bound to the buffer
1256 isValidValue = isValueWithinBufferOrZero(inDataPtr, m_inBufferAllocSize, outValuePtr, outValueSize);
1257
1258 if (isValidValue)
1259 logMsg << ", OK, written within the memory range bound to the buffer";
1260 }
1261 }
1262 }
1263
1264 if (!isValidValue)
1265 {
1266 // Check if we are satisfying the [0, 0, 0, x] pattern, where x may be either 0 or 1,
1267 // or the maximum representable positive integer value (if the format is integer-based).
1268
1269 const bool canMatchVec4Pattern = (isReadAccess
1270 && !isValuePartiallyOutOfBounds
1271 && (m_shaderType == SHADER_TYPE_VECTOR_COPY || m_shaderType == SHADER_TYPE_TEXEL_COPY)
1272 && ((offsetInBytes / 4 + 1) % 4 == 0 || m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32));
1273 bool matchesVec4Pattern = false;
1274
1275 if (canMatchVec4Pattern)
1276 {
1277 if (m_bufferFormat == VK_FORMAT_A2B10G10R10_UNORM_PACK32)
1278 matchesVec4Pattern = verifyOutOfBoundsVec4(outValuePtr, m_bufferFormat);
1279 else
1280 matchesVec4Pattern = verifyOutOfBoundsVec4(reinterpret_cast<deUint32*>(outValuePtr) - 3, m_bufferFormat);
1281 }
1282
1283 if (!canMatchVec4Pattern || !matchesVec4Pattern)
1284 {
1285 logMsg << ". Failed: ";
1286
1287 if (isReadAccess)
1288 {
1289 logMsg << "expected value within the buffer range or 0";
1290
1291 if (canMatchVec4Pattern)
1292 logMsg << ", or the [0, 0, 0, x] pattern";
1293 }
1294 else
1295 {
1296 logMsg << "written out of the range";
1297 }
1298
1299 allOk = false;
1300 }
1301 }
1302 }
1303 else // We are within bounds
1304 {
1305 if (isReadAccess)
1306 {
1307 if (!isExpectedValueFromInBuffer(offsetInBytes, outValuePtr, 4))
1308 {
1309 logMsg << ", Failed: unexpected value";
1310 allOk = false;
1311 }
1312 }
1313 else
1314 {
1315 // Out of bounds writes may change values within the bounds.
1316 if (!isValueWithinBufferOrZero(inDataPtr, m_inBufferAccessRange, outValuePtr, 4))
1317 {
1318 logMsg << ", Failed: unexpected value";
1319 allOk = false;
1320 }
1321 }
1322 }
1323 }
1324 }
1325
1326 log << tcu::TestLog::Message << logMsg.str() << tcu::TestLog::EndMessage;
1327
1328 return allOk;
1329 }
1330
1331 // BufferReadInstance
1332
BufferReadInstance(Context & context,Move<VkDevice> device,ShaderType shaderType,VkShaderStageFlags shaderStage,VkFormat bufferFormat,bool readFromStorage,VkDeviceSize inBufferAccessRange,bool accessOutOfBackingMemory)1333 BufferReadInstance::BufferReadInstance (Context& context,
1334 Move<VkDevice> device,
1335 ShaderType shaderType,
1336 VkShaderStageFlags shaderStage,
1337 VkFormat bufferFormat,
1338 bool readFromStorage,
1339 VkDeviceSize inBufferAccessRange,
1340 bool accessOutOfBackingMemory)
1341
1342 : BufferAccessInstance (context, device, shaderType, shaderStage, bufferFormat,
1343 readFromStorage ? BUFFER_ACCESS_TYPE_READ_FROM_STORAGE : BUFFER_ACCESS_TYPE_READ,
1344 inBufferAccessRange,
1345 RobustBufferAccessTest::s_numberOfBytesAccessed, // outBufferAccessRange
1346 accessOutOfBackingMemory)
1347 {
1348 }
1349
1350 // BufferWriteInstance
1351
BufferWriteInstance(Context & context,Move<VkDevice> device,ShaderType shaderType,VkShaderStageFlags shaderStage,VkFormat bufferFormat,VkDeviceSize writeBufferAccessRange,bool accessOutOfBackingMemory)1352 BufferWriteInstance::BufferWriteInstance (Context& context,
1353 Move<VkDevice> device,
1354 ShaderType shaderType,
1355 VkShaderStageFlags shaderStage,
1356 VkFormat bufferFormat,
1357 VkDeviceSize writeBufferAccessRange,
1358 bool accessOutOfBackingMemory)
1359
1360 : BufferAccessInstance (context, device, shaderType, shaderStage, bufferFormat,
1361 BUFFER_ACCESS_TYPE_WRITE,
1362 RobustBufferAccessTest::s_numberOfBytesAccessed, // inBufferAccessRange
1363 writeBufferAccessRange,
1364 accessOutOfBackingMemory)
1365 {
1366 }
1367
1368 // Test node creation functions
1369
getShaderStageName(VkShaderStageFlagBits shaderStage)1370 static const char* getShaderStageName (VkShaderStageFlagBits shaderStage)
1371 {
1372 switch (shaderStage)
1373 {
1374 case VK_SHADER_STAGE_VERTEX_BIT: return "vertex";
1375 case VK_SHADER_STAGE_FRAGMENT_BIT: return "fragment";
1376 case VK_SHADER_STAGE_COMPUTE_BIT: return "compute";
1377 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return "tess_control";
1378 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return "tess_eval";
1379 case VK_SHADER_STAGE_GEOMETRY_BIT: return "geometry";
1380
1381 default:
1382 DE_ASSERT(false);
1383 }
1384
1385 return DE_NULL;
1386 }
1387
addBufferAccessTests(tcu::TestContext & testCtx,tcu::TestCaseGroup * parentNode)1388 static void addBufferAccessTests (tcu::TestContext& testCtx, tcu::TestCaseGroup* parentNode)
1389 {
1390 struct BufferRangeConfig
1391 {
1392 const char* name;
1393 VkDeviceSize range;
1394 };
1395
1396 const VkShaderStageFlagBits bufferAccessStages[] =
1397 {
1398 VK_SHADER_STAGE_VERTEX_BIT,
1399 VK_SHADER_STAGE_FRAGMENT_BIT,
1400 VK_SHADER_STAGE_COMPUTE_BIT,
1401 };
1402
1403 const VkFormat bufferFormats[] =
1404 {
1405 VK_FORMAT_R32_SINT,
1406 VK_FORMAT_R32_UINT,
1407 VK_FORMAT_R64_SINT,
1408 VK_FORMAT_R64_UINT,
1409 VK_FORMAT_R32_SFLOAT
1410 };
1411
1412 const VkFormat texelBufferFormats[] =
1413 {
1414 VK_FORMAT_R32G32B32A32_SINT,
1415 VK_FORMAT_R32G32B32A32_UINT,
1416 VK_FORMAT_R32G32B32A32_SFLOAT,
1417
1418 VK_FORMAT_A2B10G10R10_UNORM_PACK32
1419 };
1420
1421 const BufferRangeConfig bufferRangeConfigs[] =
1422 {
1423 { "range_1_byte", 1ull },
1424 { "range_3_bytes", 3ull },
1425 { "range_4_bytes", 4ull }, // size of float
1426 { "range_32_bytes", 32ull }, // size of half mat4
1427 };
1428
1429 const BufferRangeConfig texelBufferRangeConfigs[] =
1430 {
1431 { "range_1_texel", 1u },
1432 { "range_3_texels", 3u },
1433 };
1434
1435 const char* shaderTypeNames[SHADER_TYPE_COUNT] =
1436 {
1437 "mat4_copy",
1438 "vec4_copy",
1439 "scalar_copy",
1440 "texel_copy",
1441 };
1442
1443 for (int stageNdx = 0; stageNdx < DE_LENGTH_OF_ARRAY(bufferAccessStages); stageNdx++)
1444 {
1445 const VkShaderStageFlagBits stage = bufferAccessStages[stageNdx];
1446 de::MovePtr<tcu::TestCaseGroup> stageTests (new tcu::TestCaseGroup(testCtx, getShaderStageName(stage), ""));
1447
1448 for (int shaderTypeNdx = 0; shaderTypeNdx < SHADER_TYPE_COUNT; shaderTypeNdx++)
1449 {
1450 const VkFormat* formats;
1451 size_t formatsLength;
1452 const BufferRangeConfig* ranges;
1453 size_t rangesLength;
1454 deUint32 rangeMultiplier;
1455 de::MovePtr<tcu::TestCaseGroup> shaderTypeTests (new tcu::TestCaseGroup(testCtx, shaderTypeNames[shaderTypeNdx], ""));
1456
1457 if ((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY)
1458 {
1459 formats = texelBufferFormats;
1460 formatsLength = DE_LENGTH_OF_ARRAY(texelBufferFormats);
1461
1462 ranges = texelBufferRangeConfigs;
1463 rangesLength = DE_LENGTH_OF_ARRAY(texelBufferRangeConfigs);
1464 }
1465 else
1466 {
1467 formats = bufferFormats;
1468 formatsLength = DE_LENGTH_OF_ARRAY(bufferFormats);
1469
1470 ranges = bufferRangeConfigs;
1471 rangesLength = DE_LENGTH_OF_ARRAY(bufferRangeConfigs);
1472 }
1473
1474 for (size_t formatNdx = 0; formatNdx < formatsLength; formatNdx++)
1475 {
1476 const VkFormat bufferFormat = formats[formatNdx];
1477
1478 rangeMultiplier = ((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY) ? mapVkFormat(bufferFormat).getPixelSize() : 1;
1479
1480 if (!isFloatFormat(bufferFormat) && ((ShaderType)shaderTypeNdx) == SHADER_TYPE_MATRIX_COPY)
1481 {
1482 // Use SHADER_TYPE_MATRIX_COPY with floating-point formats only
1483 break;
1484 }
1485
1486 const std::string formatName = getFormatName(bufferFormat);
1487 de::MovePtr<tcu::TestCaseGroup> formatTests (new tcu::TestCaseGroup(testCtx, de::toLower(formatName.substr(10)).c_str(), ""));
1488
1489 de::MovePtr<tcu::TestCaseGroup> uboReadTests (new tcu::TestCaseGroup(testCtx, "oob_uniform_read", ""));
1490 de::MovePtr<tcu::TestCaseGroup> ssboReadTests (new tcu::TestCaseGroup(testCtx, "oob_storage_read", ""));
1491 de::MovePtr<tcu::TestCaseGroup> ssboWriteTests (new tcu::TestCaseGroup(testCtx, "oob_storage_write", ""));
1492
1493 for (size_t rangeNdx = 0; rangeNdx < rangesLength; rangeNdx++)
1494 {
1495 const BufferRangeConfig& rangeConfig = ranges[rangeNdx];
1496 const VkDeviceSize rangeInBytes = rangeConfig.range * rangeMultiplier;
1497
1498 uboReadTests->addChild(new RobustBufferReadTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, false, false));
1499 ssboReadTests->addChild(new RobustBufferReadTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, true, false));
1500 ssboWriteTests->addChild(new RobustBufferWriteTest(testCtx, rangeConfig.name, "", stage, (ShaderType)shaderTypeNdx, bufferFormat, rangeInBytes, false));
1501
1502 }
1503
1504 formatTests->addChild(uboReadTests.release());
1505 formatTests->addChild(ssboReadTests.release());
1506 formatTests->addChild(ssboWriteTests.release());
1507
1508 shaderTypeTests->addChild(formatTests.release());
1509 }
1510
1511 // Read/write out of the memory that backs the buffer
1512 {
1513 de::MovePtr<tcu::TestCaseGroup> outOfAllocTests (new tcu::TestCaseGroup(testCtx, "out_of_alloc", ""));
1514
1515 const VkFormat format = (((ShaderType)shaderTypeNdx == SHADER_TYPE_TEXEL_COPY ) ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R32_SFLOAT);
1516
1517 outOfAllocTests->addChild(new RobustBufferReadTest(testCtx, "oob_uniform_read", "", stage, (ShaderType)shaderTypeNdx, format, 16, false, true));
1518 outOfAllocTests->addChild(new RobustBufferReadTest(testCtx, "oob_storage_read", "", stage, (ShaderType)shaderTypeNdx, format, 16, true, true));
1519 outOfAllocTests->addChild(new RobustBufferWriteTest(testCtx, "oob_storage_write", "", stage, (ShaderType)shaderTypeNdx, format, 16, true));
1520
1521 shaderTypeTests->addChild(outOfAllocTests.release());
1522 }
1523
1524 stageTests->addChild(shaderTypeTests.release());
1525 }
1526 parentNode->addChild(stageTests.release());
1527 }
1528 }
1529
createBufferAccessTests(tcu::TestContext & testCtx)1530 tcu::TestCaseGroup* createBufferAccessTests (tcu::TestContext& testCtx)
1531 {
1532 de::MovePtr<tcu::TestCaseGroup> bufferAccessTests (new tcu::TestCaseGroup(testCtx, "buffer_access", ""));
1533
1534 addBufferAccessTests(testCtx, bufferAccessTests.get());
1535
1536 return bufferAccessTests.release();
1537 }
1538
1539 } // robustness
1540 } // vkt
1541