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