1 #ifndef _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP
2 #define _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP
3 /*-------------------------------------------------------------------------
4 * Vulkan Conformance Tests
5 * ------------------------
7 * Copyright (c) 2015 Google Inc.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 * \brief Compute Shader Based Test Case Utility Structs/Functions
24 *//*--------------------------------------------------------------------*/
27 #include "deFloat16.h"
28 #include "deRandom.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuVector.hpp"
31 #include "tcuTestLog.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vktSpvAsmUtils.hpp"
43 namespace SpirVAssembly
63 BUFFERTYPE_ATOMIC_RET,
68 static void fillRandomScalars (de::Random& rnd, deInt32 minValue, deInt32 maxValue, deInt32* dst, deInt32 numValues)
70 for (int i = 0; i < numValues; i++)
71 dst[i] = rnd.getInt(minValue, maxValue);
74 /*--------------------------------------------------------------------*//*!
75 * \brief Concrete class for an input/output storage buffer object used for OpAtomic tests
76 *//*--------------------------------------------------------------------*/
77 class OpAtomicBuffer : public BufferInterface
80 OpAtomicBuffer (const deUint32 numInputElements, const deUint32 numOuptutElements, const OpAtomicType opAtomic, const BufferType type)
81 : m_numInputElements (numInputElements)
82 , m_numOutputElements (numOuptutElements)
83 , m_opAtomic (opAtomic)
87 void getBytes (std::vector<deUint8>& bytes) const
89 std::vector<deInt32> inputInts (m_numInputElements, 0);
90 de::Random rnd (m_opAtomic);
92 fillRandomScalars(rnd, 1, 100, &inputInts.front(), m_numInputElements);
94 // Return input values as is
95 if (m_type == BUFFERTYPE_INPUT)
97 size_t inputSize = m_numInputElements * sizeof(deInt32);
99 bytes.resize(inputSize);
100 deMemcpy(&bytes.front(), &inputInts.front(), inputSize);
102 // Calculate expected output values
103 else if (m_type == BUFFERTYPE_EXPECTED)
105 size_t outputSize = m_numOutputElements * sizeof(deInt32);
106 bytes.resize(outputSize, 0xffu);
108 for (size_t ndx = 0; ndx < m_numInputElements; ndx++)
110 deInt32* const bytesAsInt = reinterpret_cast<deInt32*>(&bytes.front());
114 case OPATOMIC_IADD: bytesAsInt[0] += inputInts[ndx]; break;
115 case OPATOMIC_ISUB: bytesAsInt[0] -= inputInts[ndx]; break;
116 case OPATOMIC_IINC: bytesAsInt[0]++; break;
117 case OPATOMIC_IDEC: bytesAsInt[0]--; break;
118 case OPATOMIC_LOAD: bytesAsInt[ndx] = inputInts[ndx]; break;
119 case OPATOMIC_STORE: bytesAsInt[ndx] = inputInts[ndx]; break;
120 case OPATOMIC_COMPEX: bytesAsInt[ndx] = (inputInts[ndx] % 2) == 0 ? -1 : 1; break;
121 default: DE_FATAL("Unknown OpAtomic type");
125 else if (m_type == BUFFERTYPE_ATOMIC_RET)
127 bytes.resize(m_numInputElements * sizeof(deInt32), 0xff);
129 if (m_opAtomic == OPATOMIC_COMPEX)
131 deInt32* const bytesAsInt = reinterpret_cast<deInt32*>(&bytes.front());
132 for (size_t ndx = 0; ndx < m_numInputElements; ndx++)
133 bytesAsInt[ndx] = inputInts[ndx] % 2;
137 DE_FATAL("Unknown buffer type");
140 size_t getByteSize (void) const
144 case BUFFERTYPE_ATOMIC_RET:
145 case BUFFERTYPE_INPUT:
146 return m_numInputElements * sizeof(deInt32);
147 case BUFFERTYPE_EXPECTED:
148 return m_numOutputElements * sizeof(deInt32);
150 DE_FATAL("Unknown buffer type");
155 template <int OpAtomic>
156 static bool compareWithRetvals (const std::vector<Resource>& inputs, const std::vector<AllocationSp>& outputAllocs, const std::vector<Resource>& expectedOutputs, tcu::TestLog& log)
158 if (outputAllocs.size() != 2 || inputs.size() != 1)
159 DE_FATAL("Wrong number of buffers to compare");
161 for (size_t i = 0; i < outputAllocs.size(); ++i)
163 const deUint32* values = reinterpret_cast<deUint32*>(outputAllocs[i]->getHostPtr());
165 if (i == 1 && OpAtomic != OPATOMIC_COMPEX)
167 // BUFFERTYPE_ATOMIC_RET for arithmetic operations must be verified manually by matching return values to inputs
168 std::vector<deUint8> inputBytes;
169 inputs[0].getBytes(inputBytes);
171 const deUint32* inputValues = reinterpret_cast<deUint32*>(&inputBytes.front());
172 const size_t inputValuesCount = inputBytes.size() / sizeof(deUint32);
174 // result of all atomic operations
175 const deUint32 resultValue = *reinterpret_cast<deUint32*>(outputAllocs[0]->getHostPtr());
177 if (!compareRetVals<OpAtomic>(inputValues, inputValuesCount, resultValue, values))
179 log << tcu::TestLog::Message << "Wrong contents of buffer with return values after atomic operation." << tcu::TestLog::EndMessage;
185 const BufferSp& expectedOutput = expectedOutputs[i].getBuffer();
186 std::vector<deUint8> expectedBytes;
188 expectedOutput->getBytes(expectedBytes);
190 if (deMemCmp(&expectedBytes.front(), values, expectedBytes.size()))
192 log << tcu::TestLog::Message << "Wrong contents of buffer after atomic operation" << tcu::TestLog::EndMessage;
200 template <int OpAtomic>
201 static bool compareRetVals (const deUint32* inputValues, const size_t inputValuesCount, const deUint32 resultValue, const deUint32* returnValues)
203 // as the order of execution is undefined, validation of return values for atomic operations is tricky:
204 // each inputValue stands for one atomic operation. Iterate through all of
205 // done operations in time, each time finding one matching current result and un-doing it.
207 std::vector<bool> operationsUndone (inputValuesCount, false);
208 deUint32 currentResult = resultValue;
210 for (size_t operationUndone = 0; operationUndone < inputValuesCount; ++operationUndone)
212 // find which of operations was done at this moment
214 for (ndx = 0; ndx < inputValuesCount; ++ndx)
216 if (operationsUndone[ndx]) continue;
218 deUint32 previousResult = currentResult;
222 // operations are undone here, so the actual opeation is reversed
223 case OPATOMIC_IADD: previousResult -= inputValues[ndx]; break;
224 case OPATOMIC_ISUB: previousResult += inputValues[ndx]; break;
225 case OPATOMIC_IINC: previousResult--; break;
226 case OPATOMIC_IDEC: previousResult++; break;
227 default: DE_FATAL("Unsupported OpAtomic type for return value compare");
230 if (previousResult == returnValues[ndx])
232 // found matching operation
233 currentResult = returnValues[ndx];
234 operationsUndone[ndx] = true;
238 if (ndx == inputValuesCount)
240 // no operation matches the current result value
248 const deUint32 m_numInputElements;
249 const deUint32 m_numOutputElements;
250 const OpAtomicType m_opAtomic;
251 const BufferType m_type;
254 /*--------------------------------------------------------------------*//*!
255 * \brief Concrete class for an input/output storage buffer object
256 *//*--------------------------------------------------------------------*/
258 class Buffer : public BufferInterface
261 Buffer (const std::vector<E>& elements)
262 : m_elements(elements)
265 void getBytes (std::vector<deUint8>& bytes) const
267 const size_t size = m_elements.size() * sizeof(E);
269 deMemcpy(&bytes.front(), &m_elements.front(), size);
272 size_t getByteSize (void) const
274 return m_elements.size() * sizeof(E);
278 std::vector<E> m_elements;
281 DE_STATIC_ASSERT(sizeof(tcu::Vec4) == 4 * sizeof(float));
283 typedef Buffer<float> Float32Buffer;
284 typedef Buffer<deFloat16> Float16Buffer;
285 typedef Buffer<deInt64> Int64Buffer;
286 typedef Buffer<deInt32> Int32Buffer;
287 typedef Buffer<deInt16> Int16Buffer;
288 typedef Buffer<deUint16> Uint16Buffer;
289 typedef Buffer<deInt8> Int8Buffer;
290 typedef Buffer<deUint32> Uint32Buffer;
291 typedef Buffer<deUint64> Uint64Buffer;
292 typedef Buffer<tcu::Vec4> Vec4Buffer;
294 typedef bool (*ComputeVerifyBinaryFunc) (const ProgramBinary& binary);
296 /*--------------------------------------------------------------------*//*!
297 * \brief Specification for a compute shader.
299 * This struct bundles SPIR-V assembly code, input and expected output
301 *//*--------------------------------------------------------------------*/
302 struct ComputeShaderSpec
304 std::string assembly;
305 std::string entryPoint;
306 std::vector<Resource> inputs;
307 std::vector<Resource> outputs;
308 tcu::IVec3 numWorkGroups;
309 SpecConstants specConstants;
310 BufferSp pushConstants;
311 std::vector<std::string> extensions;
312 VulkanFeatures requestedVulkanFeatures;
313 qpTestResult failResult;
314 std::string failMessage;
315 // If null, a default verification will be performed by comparing the memory pointed to by outputAllocations
316 // and the contents of expectedOutputs. Otherwise the function pointed to by verifyIO will be called.
317 // If true is returned, then the test case is assumed to have passed, if false is returned, then the test
318 // case is assumed to have failed. Exact meaning of failure can be customized with failResult.
319 VerifyIOFunc verifyIO;
320 ComputeVerifyBinaryFunc verifyBinary;
321 SpirvVersion spirvVersion;
324 ComputeShaderSpec (void)
325 : entryPoint ("main")
326 , pushConstants (DE_NULL)
327 , requestedVulkanFeatures ()
328 , failResult (QP_TEST_RESULT_FAIL)
329 , failMessage ("Output doesn't match with expected")
331 , verifyBinary (DE_NULL)
332 , spirvVersion (SPIRV_VERSION_1_0)
333 , coherentMemory (false)
337 /*--------------------------------------------------------------------*//*!
338 * \brief Helper functions for SPIR-V assembly shared by various tests
339 *//*--------------------------------------------------------------------*/
341 const char* getComputeAsmShaderPreamble (void);
342 const char* getComputeAsmShaderPreambleWithoutLocalSize (void);
343 std::string getComputeAsmCommonTypes (std::string blockStorageClass = "Uniform");
344 const char* getComputeAsmCommonInt64Types (void);
346 /*--------------------------------------------------------------------*//*!
347 * Declares two uniform variables (indata, outdata) of type
348 * "struct { float[] }". Depends on type "f32arr" (for "float[]").
349 *//*--------------------------------------------------------------------*/
350 const char* getComputeAsmInputOutputBuffer (void);
351 /*--------------------------------------------------------------------*//*!
352 * Declares buffer type and layout for uniform variables indata and
353 * outdata. Both of them are SSBO bounded to descriptor set 0.
354 * indata is at binding point 0, while outdata is at 1.
355 *//*--------------------------------------------------------------------*/
356 const char* getComputeAsmInputOutputBufferTraits (void);
358 bool verifyOutput (const std::vector<Resource>&,
359 const std::vector<AllocationSp>& outputAllocs,
360 const std::vector<Resource>& expectedOutputs,
363 // Creates vertex-shader assembly by specializing a boilerplate StringTemplate
365 std::string makeComputeShaderAssembly(const std::map<std::string, std::string>& fragments);
370 #endif // _VKTSPVASMCOMPUTESHADERTESTUTIL_HPP