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