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