1#ifndef _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
2#define _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
3/*-------------------------------------------------------------------------
4 * Vulkan Conformance Tests
5 * ------------------------
6 *
7 * Copyright (c) 2017 Google Inc.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Graphics pipeline and helper functions for SPIR-V assembly tests
24 *//*--------------------------------------------------------------------*/
25
26#include "tcuCommandLine.hpp"
27#include "tcuRGBA.hpp"
28
29#include "vkPrograms.hpp"
30#include "vktSpvAsmComputeShaderTestUtil.hpp"
31#include "vktSpvAsmUtils.hpp"
32#include "vktTestCaseUtil.hpp"
33
34#include "deRandom.hpp"
35#include "deSharedPtr.hpp"
36
37#include <map>
38#include <sstream>
39#include <string>
40#include <utility>
41
42namespace vkt
43{
44namespace SpirVAssembly
45{
46
47typedef vk::Unique<VkBuffer>										BufferHandleUp;
48typedef de::SharedPtr<BufferHandleUp>								BufferHandleSp;
49typedef vk::Unique<vk::VkShaderModule>								ModuleHandleUp;
50typedef de::SharedPtr<ModuleHandleUp>								ModuleHandleSp;
51typedef std::pair<std::string, vk::VkShaderStageFlagBits>			EntryToStage;
52typedef std::map<std::string, std::vector<EntryToStage> >			ModuleMap;
53typedef std::map<vk::VkShaderStageFlagBits, std::vector<deInt32> >	StageToSpecConstantMap;
54typedef std::pair<vk::VkDescriptorType, BufferSp>					Resource;
55
56enum NumberType
57{
58	NUMBERTYPE_INT32,
59	NUMBERTYPE_UINT32,
60	NUMBERTYPE_FLOAT32,
61	NUMBERTYPE_END32,		// Marks the end of 32-bit scalar types
62	NUMBERTYPE_INT16,
63	NUMBERTYPE_UINT16,
64	NUMBERTYPE_FLOAT16,
65};
66
67typedef enum RoundingModeFlags_e
68{
69	ROUNDINGMODE_RTE = 0x1,	// Round to nearest even
70	ROUNDINGMODE_RTZ = 0x2,	// Round to zero
71} RoundingModeFlags;
72
73typedef bool (*GraphicsVerifyIOFunc) (const std::vector<Resource>&		inputs,
74									  const std::vector<AllocationSp>&	outputAllocations,
75									  const std::vector<Resource>&		expectedOutputs,
76									  tcu::TestLog&						log);
77
78typedef bool (*GraphicsVerifyBinaryFunc) (const ProgramBinary&	binary);
79
80// Resources used by graphics-pipeline-based tests.
81struct GraphicsResources
82{
83	// Resources used as inputs.
84	std::vector<Resource>		inputs;
85	// Resources used as outputs. The data supplied will be used as
86	// the expected outputs for the corresponding bindings by default.
87	// If other behaviors are needed, please provide a custom verifyIO.
88	std::vector<Resource>		outputs;
89	// If null, a default verification will be performed by comparing the
90	// memory pointed to by outputAllocations  and the contents of
91	// expectedOutputs. Otherwise the function pointed to by verifyIO will
92	// be called. If true is returned, then the test case is assumed to
93	// have passed, if false is returned, then the test case is assumed
94	// to have failed.
95	GraphicsVerifyIOFunc		verifyIO;
96	GraphicsVerifyBinaryFunc	verifyBinary;
97	SpirvVersion				spirvVersion;
98
99							GraphicsResources()
100								: verifyIO		(DE_NULL)
101								, verifyBinary	(DE_NULL)
102								, spirvVersion	(SPIRV_VERSION_1_0)
103							{}
104};
105
106// Interface data type.
107struct IFDataType
108{
109						IFDataType			(deUint32 numE, NumberType elementT)
110							: numElements	(numE)
111							, elementType	(elementT)
112						{
113							DE_ASSERT(numE > 0 && numE < 5);
114							DE_ASSERT(elementT != NUMBERTYPE_END32);
115						}
116
117						IFDataType			(const IFDataType& that)
118							: numElements	(that.numElements)
119							, elementType	(that.elementType)
120						{}
121
122	deUint32			getElementNumBytes	(void) const;
123	deUint32			getNumBytes			(void) const { return numElements * getElementNumBytes(); }
124
125	vk::VkFormat		getVkFormat			(void) const;
126
127	tcu::TextureFormat	getTextureFormat	(void) const;
128
129	std::string			str					(void) const;
130
131	bool				elementIs32bit		(void) const { return elementType < NUMBERTYPE_END32; }
132	bool				isVector			(void) const { return numElements > 1; }
133
134	deUint32			numElements;
135	NumberType			elementType;
136};
137
138typedef std::pair<IFDataType, BufferSp>			Interface;
139
140// Interface variables used by graphics-pipeline-based tests.
141class GraphicsInterfaces
142{
143public:
144						GraphicsInterfaces	()
145							: rndMode	(static_cast<RoundingModeFlags>(0))
146						{}
147
148						GraphicsInterfaces	(const GraphicsInterfaces& that)
149							: inputs	(that.inputs)
150							, outputs	(that.outputs)
151							, rndMode	(that.rndMode)
152						{}
153
154	void				setInputOutput		(const Interface& input, const Interface&  output)
155						{
156							inputs.clear();
157							outputs.clear();
158							inputs.push_back(input);
159							outputs.push_back(output);
160						}
161
162	const IFDataType&	getInputType		(void) const
163						{
164							DE_ASSERT(inputs.size() == 1);
165							return inputs.front().first;
166						}
167
168	const IFDataType&	getOutputType		(void) const
169						{
170							DE_ASSERT(outputs.size() == 1);
171							return outputs.front().first;
172						}
173
174	const BufferSp&		getInputBuffer		(void) const
175						{
176							DE_ASSERT(inputs.size() == 1);
177							return inputs.front().second;
178						}
179
180	const BufferSp&		getOutputBuffer		(void) const
181						{
182							DE_ASSERT(outputs.size() == 1);
183							return outputs.front().second;
184						}
185
186	bool				empty				(void) const
187						{
188							return inputs.size() == 0;
189						}
190
191	void				setRoundingMode		(RoundingModeFlags flag)
192						{
193							rndMode = flag;
194						}
195	RoundingModeFlags	getRoundingMode		(void) const
196						{
197							return rndMode;
198						}
199private:
200	// vector<Interface> acts as a null-able Interface here. Canonically we should use
201	// std::unique_ptr, but sadly we cannot leverage C++11 in dEQP. dEQP has its own
202	// de::UniquePtr, but still cumbersome to use in InstanceContext and do copies
203	// at various places.
204	// Public methods should make sure that there are less than two elements in both
205	// members and both members have the same number of elements.
206	std::vector<Interface>	inputs;
207	std::vector<Interface>	outputs;
208	RoundingModeFlags		rndMode;
209
210};
211
212struct PushConstants
213{
214public:
215							PushConstants (void)
216							{}
217
218							PushConstants (const PushConstants& that)
219								: pcs	(that.pcs)
220							{}
221
222	void					setPushConstant	(const BufferSp& pc)
223							{
224								pcs.clear();
225								pcs.push_back(pc);
226							}
227
228	bool					empty (void) const
229							{
230								return pcs.empty();
231							}
232
233	const BufferSp&			getBuffer(void) const
234							{
235								DE_ASSERT(pcs.size() == 1);
236								return pcs[0];
237							}
238
239private:
240	// Right now we only support one field in the push constant block.
241	std::vector<BufferSp>	pcs;
242};
243
244// Returns the corresponding buffer usage flag bit for the given descriptor type.
245VkBufferUsageFlagBits getMatchingBufferUsageFlagBit(VkDescriptorType dType);
246
247// Context for a specific test instantiation. For example, an instantiation
248// may test colors yellow/magenta/cyan/mauve in a tesselation shader
249// with an entry point named 'main_to_the_main'
250struct InstanceContext
251{
252	// Map of modules to what entry_points we care to use from those modules.
253	ModuleMap								moduleMap;
254	tcu::RGBA								inputColors[4];
255	tcu::RGBA								outputColors[4];
256	// Concrete SPIR-V code to test via boilerplate specialization.
257	std::map<std::string, std::string>		testCodeFragments;
258	StageToSpecConstantMap					specConstants;
259	bool									hasTessellation;
260	vk::VkShaderStageFlagBits				requiredStages;
261	std::vector<std::string>				requiredDeviceExtensions;
262	std::vector<std::string>				requiredDeviceFeatures;
263	VulkanFeatures							requestedFeatures;
264	PushConstants							pushConstants;
265	// Specifies the (one or more) stages that use a customized shader code.
266	VkShaderStageFlags						customizedStages;
267	// Possible resources used by the graphics pipeline.
268	// If it is not empty, a single descriptor set (number 0) will be allocated
269	// to point to all resources specified. Binding numbers are allocated in
270	// accord with the resources' order in the vector; outputs are allocated
271	// after inputs.
272	GraphicsResources						resources;
273	// Possible interface variables use by the graphics pipeline.
274	// If it is not empty, input/output variables will be set up for shader stages
275	// in the test. Both the input and output variable will take location #2 in the
276	// pipeline for all stages, except that the output variable in the fragment
277	// stage will take location #1.
278	GraphicsInterfaces						interfaces;
279	qpTestResult							failResult;
280	std::string								failMessageTemplate;	//!< ${reason} in the template will be replaced with a detailed failure message
281
282	InstanceContext (const tcu::RGBA							(&inputs)[4],
283					 const tcu::RGBA							(&outputs)[4],
284					 const std::map<std::string, std::string>&	testCodeFragments_,
285					 const StageToSpecConstantMap&				specConstants_,
286					 const PushConstants&						pushConsants_,
287					 const GraphicsResources&					resources_,
288					 const GraphicsInterfaces&					interfaces_,
289					 const std::vector<std::string>&			extensions_,
290					 const std::vector<std::string>&			features_,
291					 VulkanFeatures								vulkanFeatures_,
292					 VkShaderStageFlags							customizedStages_);
293
294	InstanceContext (const InstanceContext& other);
295
296	std::string getSpecializedFailMessage (const std::string& failureReason);
297};
298
299// A description of a shader to be used for a single stage of the graphics pipeline.
300struct ShaderElement
301{
302	// The module that contains this shader entrypoint.
303	std::string					moduleName;
304
305	// The name of the entrypoint.
306	std::string					entryName;
307
308	// Which shader stage this entry point represents.
309	vk::VkShaderStageFlagBits	stage;
310
311	ShaderElement (const std::string& moduleName_, const std::string& entryPoint_, vk::VkShaderStageFlagBits shaderStage_);
312};
313
314template <typename T>
315const std::string numberToString (T number)
316{
317	std::stringstream ss;
318	ss << number;
319	return ss.str();
320}
321
322// Performs a bitwise copy of source to the destination type Dest.
323template <typename Dest, typename Src>
324Dest bitwiseCast(Src source)
325{
326  Dest dest;
327  DE_STATIC_ASSERT(sizeof(source) == sizeof(dest));
328  deMemcpy(&dest, &source, sizeof(dest));
329  return dest;
330}
331
332template<typename T>	T			randomScalar	(de::Random& rnd, T minValue, T maxValue);
333template<> inline		float		randomScalar	(de::Random& rnd, float minValue, float maxValue)		{ return rnd.getFloat(minValue, maxValue);	}
334template<> inline		deInt32		randomScalar	(de::Random& rnd, deInt32 minValue, deInt32 maxValue)	{ return rnd.getInt(minValue, maxValue);	}
335
336
337void getDefaultColors (tcu::RGBA (&colors)[4]);
338
339void getHalfColorsFullAlpha (tcu::RGBA (&colors)[4]);
340
341void getInvertedDefaultColors (tcu::RGBA (&colors)[4]);
342
343// Creates fragments that specialize into a simple pass-through shader (of any kind).
344std::map<std::string, std::string> passthruFragments(void);
345
346void createCombinedModule(vk::SourceCollections& dst, InstanceContext);
347
348// This has two shaders of each stage. The first
349// is a passthrough, the second inverts the color.
350void createMultipleEntries(vk::SourceCollections& dst, InstanceContext);
351
352// Turns a statically sized array of ShaderElements into an instance-context
353// by setting up the mapping of modules to their contained shaders and stages.
354// The inputs and expected outputs are given by inputColors and outputColors
355template<size_t N>
356InstanceContext createInstanceContext (const ShaderElement							(&elements)[N],
357									   const tcu::RGBA								(&inputColors)[4],
358									   const tcu::RGBA								(&outputColors)[4],
359									   const std::map<std::string, std::string>&	testCodeFragments,
360									   const StageToSpecConstantMap&				specConstants,
361									   const PushConstants&							pushConstants,
362									   const GraphicsResources&						resources,
363									   const GraphicsInterfaces&					interfaces,
364									   const std::vector<std::string>&				extensions,
365									   const std::vector<std::string>&				features,
366									   VulkanFeatures								vulkanFeatures,
367									   VkShaderStageFlags							customizedStages,
368									   const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
369									   const std::string&							failMessageTemplate	= std::string())
370{
371	InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources, interfaces, extensions, features, vulkanFeatures, customizedStages);
372	for (size_t i = 0; i < N; ++i)
373	{
374		ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
375		ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage);
376	}
377	ctx.failResult				= failResult;
378	if (!failMessageTemplate.empty())
379		ctx.failMessageTemplate	= failMessageTemplate;
380	return ctx;
381}
382
383// The same as createInstanceContext above, without extensions, spec constants, and resources.
384template<size_t N>
385inline InstanceContext createInstanceContext (const ShaderElement						(&elements)[N],
386											  tcu::RGBA									(&inputColors)[4],
387											  const tcu::RGBA							(&outputColors)[4],
388											  const std::map<std::string, std::string>&	testCodeFragments)
389{
390	return createInstanceContext(elements, inputColors, outputColors, testCodeFragments,
391								 StageToSpecConstantMap(), PushConstants(), GraphicsResources(),
392								 GraphicsInterfaces(), std::vector<std::string>(), std::vector<std::string>(),
393								 VulkanFeatures(), vk::VK_SHADER_STAGE_ALL);
394}
395
396// The same as createInstanceContext above, but with default colors.
397template<size_t N>
398InstanceContext createInstanceContext (const ShaderElement							(&elements)[N],
399									   const std::map<std::string, std::string>&	testCodeFragments)
400{
401	tcu::RGBA defaultColors[4];
402	getDefaultColors(defaultColors);
403	return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
404}
405
406
407void addShaderCodeCustomVertex(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
408void addShaderCodeCustomTessControl(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
409void addShaderCodeCustomTessEval(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
410void addShaderCodeCustomGeometry(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
411void addShaderCodeCustomFragment(vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
412
413void createTestsForAllStages (const std::string&						name,
414							  const tcu::RGBA							(&inputColors)[4],
415							  const tcu::RGBA							(&outputColors)[4],
416							  const std::map<std::string, std::string>&	testCodeFragments,
417							  const std::vector<deInt32>&				specConstants,
418							  const PushConstants&						pushConstants,
419							  const GraphicsResources&					resources,
420							  const GraphicsInterfaces&					interfaces,
421							  const std::vector<std::string>&			extensions,
422							  const std::vector<std::string>&			features,
423							  VulkanFeatures							vulkanFeatures,
424							  tcu::TestCaseGroup*						tests,
425							  const qpTestResult						failResult			= QP_TEST_RESULT_FAIL,
426							  const std::string&						failMessageTemplate	= std::string());
427
428inline void createTestsForAllStages (const std::string&							name,
429									 const tcu::RGBA							(&inputColors)[4],
430									 const tcu::RGBA							(&outputColors)[4],
431									 const std::map<std::string, std::string>&	testCodeFragments,
432									 tcu::TestCaseGroup*						tests,
433									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
434									 const std::string&							failMessageTemplate	= std::string())
435{
436	std::vector<deInt32>		noSpecConstants;
437	PushConstants				noPushConstants;
438	GraphicsResources			noResources;
439	GraphicsInterfaces			noInterfaces;
440	std::vector<std::string>	noExtensions;
441	std::vector<std::string>	noFeatures;
442
443	createTestsForAllStages(
444			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
445			noResources, noInterfaces, noExtensions, noFeatures, VulkanFeatures(),
446			tests, failResult, failMessageTemplate);
447}
448
449inline void createTestsForAllStages (const std::string&							name,
450									 const tcu::RGBA							(&inputColors)[4],
451									 const tcu::RGBA							(&outputColors)[4],
452									 const std::map<std::string, std::string>&	testCodeFragments,
453									 const std::vector<deInt32>&				specConstants,
454									 tcu::TestCaseGroup*						tests,
455									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
456									 const std::string&							failMessageTemplate	= std::string())
457{
458	PushConstants					noPushConstants;
459	GraphicsResources				noResources;
460	GraphicsInterfaces				noInterfaces;
461	std::vector<std::string>		noExtensions;
462	std::vector<std::string>		noFeatures;
463
464	createTestsForAllStages(
465			name, inputColors, outputColors, testCodeFragments, specConstants, noPushConstants,
466			noResources, noInterfaces, noExtensions, noFeatures, VulkanFeatures(),
467			tests, failResult, failMessageTemplate);
468}
469
470inline void createTestsForAllStages (const std::string&							name,
471									 const tcu::RGBA							(&inputColors)[4],
472									 const tcu::RGBA							(&outputColors)[4],
473									 const std::map<std::string, std::string>&	testCodeFragments,
474									 const GraphicsResources&					resources,
475									 const std::vector<std::string>&			extensions,
476									 tcu::TestCaseGroup*						tests,
477									 VulkanFeatures								vulkanFeatures		= VulkanFeatures(),
478									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
479									 const std::string&							failMessageTemplate	= std::string())
480{
481	std::vector<deInt32>		noSpecConstants;
482	PushConstants				noPushConstants;
483	GraphicsInterfaces			noInterfaces;
484	std::vector<std::string>	noFeatures;
485
486	createTestsForAllStages(
487			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
488			resources, noInterfaces, extensions, noFeatures, vulkanFeatures,
489			tests, failResult, failMessageTemplate);
490}
491
492inline void createTestsForAllStages (const std::string& name,
493									 const tcu::RGBA							(&inputColors)[4],
494									 const tcu::RGBA							(&outputColors)[4],
495									 const std::map<std::string, std::string>&	testCodeFragments,
496									 const GraphicsInterfaces					interfaces,
497									 const std::vector<std::string>&			extensions,
498									 tcu::TestCaseGroup*						tests,
499									 VulkanFeatures								vulkanFeatures		= VulkanFeatures(),
500									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
501									 const std::string&							failMessageTemplate	= std::string())
502{
503	GraphicsResources			noResources;
504	std::vector<deInt32>		noSpecConstants;
505	std::vector<std::string>	noFeatures;
506	PushConstants				noPushConstants;
507
508	createTestsForAllStages(
509			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
510			noResources, interfaces, extensions, noFeatures, vulkanFeatures,
511			tests, failResult, failMessageTemplate);
512}
513
514inline void createTestsForAllStages (const std::string& name,
515									 const tcu::RGBA							(&inputColors)[4],
516									 const tcu::RGBA							(&outputColors)[4],
517									 const std::map<std::string, std::string>&	testCodeFragments,
518									 const PushConstants&						pushConstants,
519									 const GraphicsResources&					resources,
520									 const std::vector<std::string>&			extensions,
521									 tcu::TestCaseGroup*						tests,
522									 VulkanFeatures								vulkanFeatures		= VulkanFeatures(),
523									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
524									 const std::string&							failMessageTemplate	= std::string())
525{
526	std::vector<deInt32>			noSpecConstants;
527	GraphicsInterfaces				noInterfaces;
528	std::vector<std::string>		noFeatures;
529
530	createTestsForAllStages(
531			name, inputColors, outputColors, testCodeFragments, noSpecConstants, pushConstants,
532			resources, noInterfaces, extensions, noFeatures, vulkanFeatures,
533			tests, failResult, failMessageTemplate);
534}
535
536// Sets up and runs a Vulkan pipeline, then spot-checks the resulting image.
537// Feeds the pipeline a set of colored triangles, which then must occur in the
538// rendered image.  The surface is cleared before executing the pipeline, so
539// whatever the shaders draw can be directly spot-checked.
540tcu::TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance);
541
542// Adds a new test to group using custom fragments for the tessellation-control
543// stage and passthrough fragments for all other stages.  Uses default colors
544// for input and expected output.
545void addTessCtrlTest(tcu::TestCaseGroup* group, const char* name, const std::map<std::string, std::string>& fragments);
546
547// Given the original 32-bit float value, computes the corresponding 16-bit
548// float value under the given rounding mode flags and compares with the
549// returned 16-bit float value. Returns true if they are considered as equal.
550//
551// The following equivalence criteria are respected:
552// * Positive and negative zeros are considered equivalent.
553// * Denormalized floats are allowed to be flushed to zeros, including
554//   * Inputted 32bit denormalized float
555//   * Generated 16bit denormalized float
556// * Different bit patterns of NaNs are allowed.
557// * For the rest, require exactly the same bit pattern.
558bool compare16BitFloat (float original, deUint16 returned, RoundingModeFlags flags, tcu::TestLog& log);
559
560// Compare the returned 32-bit float against its expected value.
561//
562// The following equivalence criteria are respected:
563// * Denormalized floats are allowed to be flushed to zeros, including
564//   * The expected value itself is a denormalized float
565//   * The expected value is a denormalized float if converted to 16bit
566// * Different bit patterns of NaNs/Infs are allowed.
567// * For the rest, use C++ float equivalence check.
568bool compare32BitFloat (float expected, float returned, tcu::TestLog& log);
569
570} // SpirVAssembly
571} // vkt
572
573#endif // _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
574