1#ifndef _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP
2#define _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP
3/*-------------------------------------------------------------------------
4 * Vulkan Conformance Tests
5 * ------------------------
6 *
7 * Copyright (c) 2015 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 Compute Shader Based Test Case Utility Structs/Functions
24 *//*--------------------------------------------------------------------*/
25
26#include "deDefs.h"
27#include "deFloat16.h"
28#include "deRandom.hpp"
29#include "deSharedPtr.hpp"
30#include "tcuTestLog.hpp"
31#include "tcuVector.hpp"
32#include "vkMemUtil.hpp"
33#include "vktSpvAsmUtils.hpp"
34
35#include <string>
36#include <vector>
37#include <map>
38
39using namespace vk;
40
41namespace vkt
42{
43namespace SpirVAssembly
44{
45
46enum OpAtomicType
47{
48	OPATOMIC_IADD = 0,
49	OPATOMIC_ISUB,
50	OPATOMIC_IINC,
51	OPATOMIC_IDEC,
52	OPATOMIC_LOAD,
53	OPATOMIC_STORE,
54	OPATOMIC_COMPEX,
55
56	OPATOMIC_LAST
57};
58
59enum BufferType
60{
61	BUFFERTYPE_INPUT = 0,
62	BUFFERTYPE_EXPECTED,
63
64	BUFFERTYPE_LAST
65};
66
67static void fillRandomScalars (de::Random& rnd, deInt32 minValue, deInt32 maxValue, deInt32* dst, deInt32 numValues)
68{
69	for (int i = 0; i < numValues; i++)
70		dst[i] = rnd.getInt(minValue, maxValue);
71}
72
73typedef de::MovePtr<vk::Allocation>			AllocationMp;
74typedef de::SharedPtr<vk::Allocation>		AllocationSp;
75
76/*--------------------------------------------------------------------*//*!
77 * \brief Abstract class for an input/output storage buffer object
78 *//*--------------------------------------------------------------------*/
79class BufferInterface
80{
81public:
82	virtual				~BufferInterface	(void)				{}
83
84	virtual void		getBytes			(std::vector<deUint8>& bytes) const = 0;
85	virtual size_t		getByteSize			(void) const = 0;
86};
87
88typedef de::SharedPtr<BufferInterface>		BufferSp;
89
90/*--------------------------------------------------------------------*//*!
91* \brief Concrete class for an input/output storage buffer object used for OpAtomic tests
92*//*--------------------------------------------------------------------*/
93class OpAtomicBuffer : public BufferInterface
94{
95public:
96						OpAtomicBuffer		(const deUint32 numInputElements, const deUint32 numOuptutElements, const OpAtomicType opAtomic, const BufferType type)
97							: m_numInputElements	(numInputElements)
98							, m_numOutputElements	(numOuptutElements)
99							, m_opAtomic			(opAtomic)
100							, m_type				(type)
101						{}
102
103	void getBytes (std::vector<deUint8>& bytes) const
104	{
105		std::vector<deInt32>	inputInts	(m_numInputElements, 0);
106		de::Random				rnd			(m_opAtomic);
107
108		fillRandomScalars(rnd, 1, 100, &inputInts.front(), m_numInputElements);
109
110		// Return input values as is
111		if (m_type == BUFFERTYPE_INPUT)
112		{
113			size_t					inputSize	= m_numInputElements * sizeof(deInt32);
114
115			bytes.resize(inputSize);
116			deMemcpy(&bytes.front(), &inputInts.front(), inputSize);
117		}
118		// Calculate expected output values
119		else if (m_type == BUFFERTYPE_EXPECTED)
120		{
121			size_t					outputSize	= m_numOutputElements * sizeof(deInt32);
122			bytes.resize(outputSize, 0xffu);
123
124			for (size_t ndx = 0; ndx < m_numInputElements; ndx++)
125			{
126				deInt32* const bytesAsInt = reinterpret_cast<deInt32* const>(&bytes.front());
127
128				switch (m_opAtomic)
129				{
130					case OPATOMIC_IADD:		bytesAsInt[0] += inputInts[ndx];						break;
131					case OPATOMIC_ISUB:		bytesAsInt[0] -= inputInts[ndx];						break;
132					case OPATOMIC_IINC:		bytesAsInt[0]++;										break;
133					case OPATOMIC_IDEC:		bytesAsInt[0]--;										break;
134					case OPATOMIC_LOAD:		bytesAsInt[ndx] = inputInts[ndx];						break;
135					case OPATOMIC_STORE:	bytesAsInt[ndx] = inputInts[ndx];						break;
136					case OPATOMIC_COMPEX:	bytesAsInt[ndx] = (inputInts[ndx] % 2) == 0 ? -1 : 1;	break;
137					default:				DE_FATAL("Unknown OpAtomic type");
138				}
139			}
140		}
141		else
142			DE_FATAL("Unknown buffer type");
143	}
144
145	size_t getByteSize (void) const
146	{
147		switch (m_type)
148		{
149			case BUFFERTYPE_INPUT:
150				return m_numInputElements * sizeof(deInt32);
151			case BUFFERTYPE_EXPECTED:
152				return m_numOutputElements * sizeof(deInt32);
153			default:
154				DE_FATAL("Unknown buffer type");
155				return 0;
156		}
157	}
158
159private:
160	const deUint32		m_numInputElements;
161	const deUint32		m_numOutputElements;
162	const OpAtomicType	m_opAtomic;
163	const BufferType	m_type;
164};
165
166/*--------------------------------------------------------------------*//*!
167 * \brief Concrete class for an input/output storage buffer object
168 *//*--------------------------------------------------------------------*/
169template<typename E>
170class Buffer : public BufferInterface
171{
172public:
173						Buffer				(const std::vector<E>& elements)
174							: m_elements(elements)
175						{}
176
177	void getBytes (std::vector<deUint8>& bytes) const
178	{
179		const size_t size = m_elements.size() * sizeof(E);
180		bytes.resize(size);
181		deMemcpy(&bytes.front(), &m_elements.front(), size);
182	}
183
184	size_t getByteSize (void) const
185	{
186		return m_elements.size() * sizeof(E);
187	}
188
189private:
190	std::vector<E>		m_elements;
191};
192
193DE_STATIC_ASSERT(sizeof(tcu::Vec4) == 4 * sizeof(float));
194
195typedef Buffer<float>		Float32Buffer;
196typedef Buffer<deFloat16>	Float16Buffer;
197typedef Buffer<deInt64>		Int64Buffer;
198typedef Buffer<deInt32>		Int32Buffer;
199typedef Buffer<deInt16>		Int16Buffer;
200typedef Buffer<tcu::Vec4>	Vec4Buffer;
201
202typedef bool (*ComputeVerifyIOFunc) (const std::vector<BufferSp>&		inputs,
203									 const std::vector<AllocationSp>&	outputAllocations,
204									 const std::vector<BufferSp>&		expectedOutputs,
205									 tcu::TestLog&						log);
206
207typedef bool (*ComputeVerifyBinaryFunc) (const ProgramBinary&	binary);
208
209/*--------------------------------------------------------------------*//*!
210 * \brief Specification for a compute shader.
211 *
212 * This struct bundles SPIR-V assembly code, input and expected output
213 * together.
214 *//*--------------------------------------------------------------------*/
215struct ComputeShaderSpec
216{
217	std::string								assembly;
218	std::string								entryPoint;
219	std::vector<BufferSp>					inputs;
220	// Mapping from input index (in the inputs field) to the descriptor type.
221	std::map<deUint32, VkDescriptorType>	inputTypes;
222	std::vector<BufferSp>					outputs;
223	tcu::IVec3								numWorkGroups;
224	std::vector<deUint32>					specConstants;
225	BufferSp								pushConstants;
226	std::vector<std::string>				extensions;
227	VulkanFeatures							requestedVulkanFeatures;
228	qpTestResult							failResult;
229	std::string								failMessage;
230	// If null, a default verification will be performed by comparing the memory pointed to by outputAllocations
231	// and the contents of expectedOutputs. Otherwise the function pointed to by verifyIO will be called.
232	// If true is returned, then the test case is assumed to have passed, if false is returned, then the test
233	// case is assumed to have failed. Exact meaning of failure can be customized with failResult.
234	ComputeVerifyIOFunc						verifyIO;
235	ComputeVerifyBinaryFunc					verifyBinary;
236	SpirvVersion							spirvVersion;
237
238											ComputeShaderSpec (void)
239												: entryPoint					("main")
240												, pushConstants					(DE_NULL)
241												, requestedVulkanFeatures		()
242												, failResult					(QP_TEST_RESULT_FAIL)
243												, failMessage					("Output doesn't match with expected")
244												, verifyIO						(DE_NULL)
245												, verifyBinary					(DE_NULL)
246												, spirvVersion					(SPIRV_VERSION_1_0)
247											{}
248};
249
250/*--------------------------------------------------------------------*//*!
251 * \brief Helper functions for SPIR-V assembly shared by various tests
252 *//*--------------------------------------------------------------------*/
253
254const char* getComputeAsmShaderPreamble				(void);
255std::string getComputeAsmCommonTypes				(std::string blockStorageClass = "Uniform");
256const char*	getComputeAsmCommonInt64Types			(void);
257
258/*--------------------------------------------------------------------*//*!
259 * Declares two uniform variables (indata, outdata) of type
260 * "struct { float[] }". Depends on type "f32arr" (for "float[]").
261 *//*--------------------------------------------------------------------*/
262const char* getComputeAsmInputOutputBuffer			(void);
263/*--------------------------------------------------------------------*//*!
264 * Declares buffer type and layout for uniform variables indata and
265 * outdata. Both of them are SSBO bounded to descriptor set 0.
266 * indata is at binding point 0, while outdata is at 1.
267 *//*--------------------------------------------------------------------*/
268const char* getComputeAsmInputOutputBufferTraits	(void);
269
270bool verifyOutput									(const std::vector<BufferSp>&,
271													const std::vector<AllocationSp>& outputAllocs,
272													const std::vector<BufferSp>&		expectedOutputs,
273													tcu::TestLog&						log);
274
275} // SpirVAssembly
276} // vkt
277
278#endif // _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP
279