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