blob: 832730ee13b212c6ba3f9ae2e790d5b5017edd07 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief SPIR-V Assembly Tests for Integer Types
*//*--------------------------------------------------------------------*/
#include "vktSpvAsmTypeTests.hpp"
#include "tcuRGBA.hpp"
#include "tcuStringTemplate.hpp"
#include "vkDefs.hpp"
#include "vkMemUtil.hpp"
#include "vkPrograms.hpp"
#include "vkQueryUtil.hpp"
#include "vkRefUtil.hpp"
#include "vktTestCase.hpp"
#include "deStringUtil.hpp"
#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
#include "vktTestGroupUtil.hpp"
#include "spirv/unified1/spirv.h"
#include "spirv/unified1/GLSL.std.450.h"
#include <cmath>
#define TEST_DATASET_SIZE 10
#define UNDEFINED_SPIRV_TEST_TYPE "testtype"
namespace de
{
// Specialize template to have output as integers instead of chars
template<>
inline std::string toString<deInt8> (const deInt8& value)
{
std::ostringstream s;
s << static_cast<deInt32>(value);
return s.str();
}
template<>
inline std::string toString<deUint8> (const deUint8& value)
{
std::ostringstream s;
s << static_cast<deUint32>(value);
return s.str();
}
}
namespace vkt
{
namespace SpirVAssembly
{
using namespace vk;
using tcu::RGBA;
using std::map;
using std::string;
using std::vector;
using tcu::StringTemplate;
enum InputRange
{
RANGE_FULL = 0,
RANGE_BIT_WIDTH,
RANGE_BIT_WIDTH_SUM,
RANGE_LAST
};
enum InputWidth
{
WIDTH_DEFAULT = 0,
WIDTH_8,
WIDTH_16,
WIDTH_32,
WIDTH_64,
WIDTH_8_8,
WIDTH_8_16,
WIDTH_8_32,
WIDTH_8_64,
WIDTH_16_8,
WIDTH_16_16,
WIDTH_16_32,
WIDTH_16_64,
WIDTH_32_8,
WIDTH_32_16,
WIDTH_32_32,
WIDTH_32_64,
WIDTH_64_8,
WIDTH_64_16,
WIDTH_64_32,
WIDTH_64_64,
WIDTH_LAST
};
enum InputType
{
TYPE_I8 = 0,
TYPE_U8,
TYPE_I16,
TYPE_U16,
TYPE_I32,
TYPE_U32,
TYPE_I64,
TYPE_U64,
TYPE_LAST
};
deUint32 getConstituentIndex (deUint32 ndx, deUint32 vectorSize)
{
DE_ASSERT(vectorSize != 0u);
return (ndx / vectorSize) / (1u + (ndx % vectorSize));
}
bool isScalarInput (deUint32 spirvOperation, deUint32 numInput)
{
switch (spirvOperation)
{
case SpvOpBitFieldInsert:
return (numInput > 1);
case SpvOpBitFieldSExtract:
return (numInput > 0);
case SpvOpBitFieldUExtract:
return (numInput > 0);
default:
return false;
}
}
bool isBooleanResultTest (deUint32 spirvOperation)
{
switch (spirvOperation)
{
case SpvOpIEqual:
return true;
case SpvOpINotEqual:
return true;
case SpvOpUGreaterThan:
return true;
case SpvOpSGreaterThan:
return true;
case SpvOpUGreaterThanEqual:
return true;
case SpvOpSGreaterThanEqual:
return true;
case SpvOpULessThan:
return true;
case SpvOpSLessThan:
return true;
case SpvOpULessThanEqual:
return true;
case SpvOpSLessThanEqual:
return true;
default:
return false;
}
}
bool isConstantOrVariableTest (deUint32 spirvOperation)
{
switch (spirvOperation)
{
case SpvOpConstantNull:
return true;
case SpvOpConstant:
return true;
case SpvOpConstantComposite:
return true;
case SpvOpVariable:
return true;
case SpvOpSpecConstant:
return true;
case SpvOpSpecConstantComposite:
return true;
default:
return false;
}
}
const char* getSpvOperationStr (deUint32 spirvOperation)
{
switch (spirvOperation)
{
case SpvOpSNegate:
return "OpSNegate";
case SpvOpIAdd:
return "OpIAdd";
case SpvOpISub:
return "OpISub";
case SpvOpIMul:
return "OpIMul";
case SpvOpSDiv:
return "OpSDiv";
case SpvOpUDiv:
return "OpUDiv";
case SpvOpSRem:
return "OpSRem";
case SpvOpSMod:
return "OpSMod";
case SpvOpUMod:
return "OpUMod";
case SpvOpShiftRightLogical:
return "OpShiftRightLogical";
case SpvOpShiftRightArithmetic:
return "OpShiftRightArithmetic";
case SpvOpShiftLeftLogical:
return "OpShiftLeftLogical";
case SpvOpBitwiseOr:
return "OpBitwiseOr";
case SpvOpBitwiseXor:
return "OpBitwiseXor";
case SpvOpBitwiseAnd:
return "OpBitwiseAnd";
case SpvOpNot:
return "OpNot";
case SpvOpIEqual:
return "OpIEqual";
case SpvOpINotEqual:
return "OpINotEqual";
case SpvOpUGreaterThan:
return "OpUGreaterThan";
case SpvOpSGreaterThan:
return "OpSGreaterThan";
case SpvOpUGreaterThanEqual:
return "OpUGreaterThanEqual";
case SpvOpSGreaterThanEqual:
return "OpSGreaterThanEqual";
case SpvOpULessThan:
return "OpULessThan";
case SpvOpSLessThan:
return "OpSLessThan";
case SpvOpULessThanEqual:
return "OpULessThanEqual";
case SpvOpSLessThanEqual:
return "OpSLessThanEqual";
case SpvOpBitFieldInsert:
return "OpBitFieldInsert";
case SpvOpBitFieldSExtract:
return "OpBitFieldSExtract";
case SpvOpBitFieldUExtract:
return "OpBitFieldUExtract";
case SpvOpBitReverse:
return "OpBitReverse";
case SpvOpBitCount:
return "OpBitCount";
case SpvOpConstant:
return "OpConstant";
case SpvOpConstantComposite:
return "OpConstantComposite";
case SpvOpConstantNull:
return "OpConstantNull";
case SpvOpVariable:
return "OpVariable";
case SpvOpSpecConstant:
return "OpSpecConstant";
case SpvOpSpecConstantComposite:
return "OpSpecConstantComposite";
default:
return "";
}
}
const char* getGLSLstd450OperationStr (deUint32 spirvOperation)
{
switch (spirvOperation)
{
case GLSLstd450SAbs:
return "SAbs";
case GLSLstd450SSign:
return "SSign";
case GLSLstd450SMin:
return "SMin";
case GLSLstd450UMin:
return "UMin";
case GLSLstd450SMax:
return "SMax";
case GLSLstd450UMax:
return "UMax";
case GLSLstd450SClamp:
return "SClamp";
case GLSLstd450UClamp:
return "UClamp";
case GLSLstd450FindILsb:
return "FindILsb";
case GLSLstd450FindSMsb:
return "FindSMsb";
case GLSLstd450FindUMsb:
return "FindUMsb";
default:
DE_FATAL("Not implemented");
return "";
}
}
string getBooleanResultType (deUint32 vectorSize)
{
if (vectorSize > 1)
return "v" + de::toString(vectorSize) + "bool";
else
return "bool";
}
deUint32 getInputWidth (InputWidth inputWidth, deUint32 ndx)
{
switch (inputWidth)
{
case WIDTH_8:
DE_ASSERT(ndx < 1);
return 8;
case WIDTH_16:
DE_ASSERT(ndx < 1);
return 16;
case WIDTH_32:
DE_ASSERT(ndx < 1);
return 32;
case WIDTH_64:
DE_ASSERT(ndx < 1);
return 64;
case WIDTH_8_8:
DE_ASSERT(ndx < 2);
return 8;
case WIDTH_8_16:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 8 : 16;
case WIDTH_8_32:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 8 : 32;
case WIDTH_8_64:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 8 : 64;
case WIDTH_16_8:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 16 : 8;
case WIDTH_16_16:
DE_ASSERT(ndx < 2);
return 16;
case WIDTH_16_32:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 16 : 32;
case WIDTH_16_64:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 16 : 64;
case WIDTH_32_8:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 32 : 8;
case WIDTH_32_16:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 32 : 16;
case WIDTH_32_32:
DE_ASSERT(ndx < 2);
return 32;
case WIDTH_32_64:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 32 : 64;
case WIDTH_64_8:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 64 : 8;
case WIDTH_64_16:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 64 : 16;
case WIDTH_64_32:
DE_ASSERT(ndx < 2);
return (ndx == 0) ? 64 : 32;
case WIDTH_64_64:
DE_ASSERT(ndx < 2);
return 64;
default:
DE_FATAL("Not implemented");
return 0;
}
}
bool has8BitInputWidth (InputWidth inputWidth)
{
switch (inputWidth)
{
case WIDTH_8: return true;
case WIDTH_16: return false;
case WIDTH_32: return false;
case WIDTH_64: return false;
case WIDTH_8_8: return true;
case WIDTH_8_16: return true;
case WIDTH_8_32: return true;
case WIDTH_8_64: return true;
case WIDTH_16_8: return true;
case WIDTH_16_16: return false;
case WIDTH_16_32: return false;
case WIDTH_16_64: return false;
case WIDTH_32_8: return true;
case WIDTH_32_16: return false;
case WIDTH_32_32: return false;
case WIDTH_32_64: return false;
case WIDTH_64_8: return true;
case WIDTH_64_16: return false;
case WIDTH_64_32: return false;
case WIDTH_64_64: return false;
default: return false;
}
}
bool has16BitInputWidth (InputWidth inputWidth)
{
switch (inputWidth)
{
case WIDTH_8: return false;
case WIDTH_16: return true;
case WIDTH_32: return false;
case WIDTH_64: return false;
case WIDTH_8_8: return false;
case WIDTH_8_16: return true;
case WIDTH_8_32: return false;
case WIDTH_8_64: return false;
case WIDTH_16_8: return true;
case WIDTH_16_16: return true;
case WIDTH_16_32: return true;
case WIDTH_16_64: return true;
case WIDTH_32_8: return false;
case WIDTH_32_16: return true;
case WIDTH_32_32: return false;
case WIDTH_32_64: return false;
case WIDTH_64_8: return false;
case WIDTH_64_16: return true;
case WIDTH_64_32: return false;
case WIDTH_64_64: return false;
default: return false;
}
}
bool has64BitInputWidth (InputWidth inputWidth)
{
switch (inputWidth)
{
case WIDTH_8: return false;
case WIDTH_16: return false;
case WIDTH_32: return false;
case WIDTH_64: return true;
case WIDTH_8_8: return false;
case WIDTH_8_16: return false;
case WIDTH_8_32: return false;
case WIDTH_8_64: return true;
case WIDTH_16_8: return false;
case WIDTH_16_16: return false;
case WIDTH_16_32: return false;
case WIDTH_16_64: return true;
case WIDTH_32_8: return false;
case WIDTH_32_16: return false;
case WIDTH_32_32: return false;
case WIDTH_32_64: return true;
case WIDTH_64_8: return true;
case WIDTH_64_16: return true;
case WIDTH_64_32: return true;
case WIDTH_64_64: return true;
default: return false;
}
}
InputType getInputType (deUint32 inputWidth, bool isSigned)
{
switch (inputWidth)
{
case 8:
return (isSigned) ? TYPE_I8 : TYPE_U8;
case 16:
return (isSigned) ? TYPE_I16 : TYPE_U16;
case 32:
return (isSigned) ? TYPE_I32 : TYPE_U32;
case 64:
return (isSigned) ? TYPE_I64 : TYPE_U64;
default:
DE_FATAL("Not possible");
return TYPE_LAST;
}
}
string getOtherSizeTypes (InputType inputType, deUint32 vectorSize, InputWidth inputWidth)
{
const deUint32 inputWidthValues[] =
{
8, 16, 32, 64
};
for (deUint32 widthNdx = 0; widthNdx < DE_LENGTH_OF_ARRAY(inputWidthValues); widthNdx++)
{
const deUint32 typeWidth = inputWidthValues[widthNdx];
const InputType typeUnsigned = getInputType(typeWidth, false);
const InputType typeSigned = getInputType(typeWidth, true);
if ((inputType == typeUnsigned) || (inputType == typeSigned))
{
const bool isSigned = (inputType == typeSigned);
const string signPrefix = (isSigned) ? "i" : "u";
const string signBit = (isSigned) ? "1" : "0";
string str = "";
if (has8BitInputWidth(inputWidth) && typeWidth != 8)
{
// 8-bit scalar type
str += "%" + signPrefix + "8 = OpTypeInt 8 " + signBit + "\n";
// 8-bit vector type
if (vectorSize > 1)
str += "%v" + de::toString(vectorSize) + signPrefix + "8 = OpTypeVector %" + signPrefix + "8 " + de::toString(vectorSize) + "\n";
}
if (has16BitInputWidth(inputWidth) && typeWidth != 16)
{
// 16-bit scalar type
str += "%" + signPrefix + "16 = OpTypeInt 16 " + signBit + "\n";
// 16-bit vector type
if (vectorSize > 1)
str += "%v" + de::toString(vectorSize) + signPrefix + "16 = OpTypeVector %" + signPrefix + "16 " + de::toString(vectorSize) + "\n";
}
if (has64BitInputWidth(inputWidth) && typeWidth != 64)
{
// 64-bit scalar type
str += "%" + signPrefix + "64 = OpTypeInt 64 " + signBit + "\n";
// 64-bit vector type
if (vectorSize > 1)
str += "%v" + de::toString(vectorSize) + signPrefix + "64 = OpTypeVector %" + signPrefix + "64 " + de::toString(vectorSize) + "\n";
}
return str;
}
}
DE_FATAL("Not possible");
return "";
}
string getSpirvCapabilityStr (const char* spirvCapability, InputWidth inputWidth)
{
string str = "";
if (spirvCapability)
{
if (has8BitInputWidth(inputWidth) || deStringEqual("Int8", spirvCapability))
str += "OpCapability Int8\n";
if (has16BitInputWidth(inputWidth) || deStringEqual("Int16", spirvCapability))
str += "OpCapability Int16\n";
if (has64BitInputWidth(inputWidth) || deStringEqual("Int64", spirvCapability))
str += "OpCapability Int64\n";
if (deStringEqual("Int8", spirvCapability))
str += "OpCapability UniformAndStorageBuffer8BitAccess\n";
if (deStringEqual("Int16", spirvCapability))
str += "OpCapability UniformAndStorageBuffer16BitAccess\n";
}
else
{
if (has8BitInputWidth(inputWidth))
str += "OpCapability Int8\n";
if (has16BitInputWidth(inputWidth))
str += "OpCapability Int16\n";
if (has64BitInputWidth(inputWidth))
str += "OpCapability Int64\n";
}
return str;
}
string getBinaryFullOperationWithInputWidthStr (string resultName, string spirvOperation, InputType inputType, string spirvTestType, deUint32 vectorSize, InputWidth inputWidth)
{
const deUint32 inputWidthValues[] =
{
8, 16, 32, 64
};
for (deUint32 widthNdx = 0; widthNdx < DE_LENGTH_OF_ARRAY(inputWidthValues); widthNdx++)
{
const deUint32 typeWidth = inputWidthValues[widthNdx];
const InputType typeUnsigned = getInputType(typeWidth, false);
const InputType typeSigned = getInputType(typeWidth, true);
if ((inputType == typeUnsigned) || (inputType == typeSigned))
{
const bool isSigned = (inputType == typeSigned);
const string signPrefix = (isSigned) ? "i" : "u";
const string typePrefix = (vectorSize == 1) ? "%" : "%v" + de::toString(vectorSize);
const deUint32 input1Width = getInputWidth(inputWidth, 0);
const string inputTypeStr = (input1Width == typeWidth) ? "%testtype"
: typePrefix + signPrefix + de::toString(input1Width);
string str = "";
// Create intermediate value with different width
if (input1Width != typeWidth)
str += "%input1_val_" + de::toString(input1Width) + " = OpSConvert " + inputTypeStr + " %input1_val\n";
// Input with potentially different width
const string input1Str = "%input1_val" + ((input1Width != typeWidth) ? "_" + de::toString(input1Width) : "");
str += resultName + " = " + spirvOperation + " %" + spirvTestType + " %input0_val " + input1Str + "\n";
return str;
}
}
DE_FATAL("Not possible");
return "";
}
string getFullOperationWithDifferentInputWidthStr (string resultName, string spirvOperation, InputType inputType, string spirvTestType, InputWidth inputWidth, bool isQuaternary)
{
const bool isSigned = (inputType == TYPE_I32);
const deUint32 offsetWidth = getInputWidth(inputWidth, 0);
const deUint32 countWidth = getInputWidth(inputWidth, 1);
const string offsetType = ((isSigned) ? "i" : "u") + de::toString(offsetWidth);
const string countType = ((isSigned) ? "i" : "u") + de::toString(countWidth);
const string offsetNdx = (isQuaternary) ? "2" : "1";
const string countNdx = (isQuaternary) ? "3" : "2";
string str = "";
// Create intermediate values with different width
if (offsetWidth != 32)
str += "%input" + offsetNdx + "_val_" + de::toString(offsetWidth) + " = OpSConvert %" + offsetType + " %input" + offsetNdx + "_val\n";
if (countWidth != 32)
str += "%input" + countNdx + "_val_" + de::toString(countWidth) + " = OpSConvert %" + countType + " %input" + countNdx + "_val\n";
// Inputs with potentially different width
const string offsetStr = "%input" + offsetNdx + "_val" + ((offsetWidth != 32) ? "_" + de::toString(offsetWidth) : "");
const string countStr = "%input" + countNdx + "_val" + ((countWidth != 32) ? "_" + de::toString(countWidth) : "");
if (isQuaternary)
str += resultName + " = " + spirvOperation + " %" + spirvTestType + " %input0_val %input1_val " + offsetStr + " " + countStr +"\n";
else
str += resultName + " = " + spirvOperation + " %" + spirvTestType + " %input0_val " + offsetStr + " " + countStr +"\n";
return str;
}
static inline void requiredFeaturesFromStrings(const std::vector<std::string> &features, VulkanFeatures &requestedFeatures)
{
for (deUint32 featureNdx = 0; featureNdx < features.size(); ++featureNdx)
{
const std::string& feature = features[featureNdx];
if (feature == "shaderInt16")
requestedFeatures.coreFeatures.shaderInt16 = VK_TRUE;
else if (feature == "shaderInt64")
requestedFeatures.coreFeatures.shaderInt64 = VK_TRUE;
else
DE_ASSERT(0); // Not implemented. Don't add to here. Just use VulkanFeatures
}
}
template <class T>
class SpvAsmTypeTests : public tcu::TestCaseGroup
{
public:
typedef T (*OpUnaryFuncType) (T);
typedef T (*OpBinaryFuncType) (T, T);
typedef T (*OpTernaryFuncType) (T, T, T);
typedef T (*OpQuaternaryFuncType) (T, T, T, T);
typedef bool (*UnaryFilterFuncType) (T);
typedef bool (*BinaryFilterFuncType) (T, T);
typedef bool (*TernaryFilterFuncType) (T, T, T);
typedef bool (*QuaternaryFilterFuncType) (T, T, T, T);
SpvAsmTypeTests (tcu::TestContext& testCtx,
const char* name,
const char* description,
const char* deviceFeature,
const char* spirvCapability,
const char* spirvType,
InputType inputType,
deUint32 typeSize,
deUint32 vectorSize);
~SpvAsmTypeTests (void);
void createTests (const char* testName,
deUint32 spirvOperation,
OpUnaryFuncType op,
UnaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart = false);
void createTests (const char* testName,
deUint32 spirvOperation,
OpBinaryFuncType op,
BinaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart = false);
void createTests (const char* testName,
deUint32 spirvOperation,
OpTernaryFuncType op,
TernaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart = false);
void createTests (const char* testName,
deUint32 spirvOperation,
OpQuaternaryFuncType op,
QuaternaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart = false);
void createSwitchTests (void);
void getConstantDataset (vector<T> inputDataset,
vector<T>& outputDataset,
deUint32 spirvOperation);
virtual void getDataset (vector<T>& input, deUint32 numElements) = 0;
virtual void pushResource (vector<Resource>& resource,
vector<T>& data) = 0;
static bool filterNone (T a);
static bool filterNone (T a, T b);
static bool filterNone (T a, T b, T c);
static bool filterNone (T a, T b, T c, T d);
static bool filterZero (T a, T b);
static bool filterNegativesAndZero (T a, T b);
static bool filterMinGtMax (T, T a, T b);
static T zero (T);
static T zero (T, T);
static T zero (T, T, T);
static T zero (T, T, T, T);
static string replicate (const std::string& replicant,
const deUint32 count);
protected:
de::Random m_rnd;
T m_cases[3];
private:
std::string createInputDecoration (deUint32 numInput);
std::string createInputPreMain (deUint32 numInput,
deUint32 spirvOpertaion);
std::string createConstantDeclaration (vector<T>& dataset,
deUint32 spirvOperation);
std::string createInputTestfun (deUint32 numInput,
deUint32 spirvOpertaion);
deUint32 combine (GraphicsResources& resources,
vector<T>& data,
OpUnaryFuncType operation,
UnaryFilterFuncType filter,
InputRange inputRange);
deUint32 combine (GraphicsResources& resources,
vector<T>& data,
OpBinaryFuncType operation,
BinaryFilterFuncType filter,
InputRange inputRange);
deUint32 combine (GraphicsResources& resources,
vector<T>& data,
OpTernaryFuncType operation,
TernaryFilterFuncType filter,
InputRange inputRange);
deUint32 combine (GraphicsResources& resources,
vector<T>& data,
OpQuaternaryFuncType operation,
QuaternaryFilterFuncType filter,
InputRange inputRange);
deUint32 fillResources (GraphicsResources& resources,
vector<T>& data);
void createStageTests (const char* testName,
GraphicsResources& resources,
deUint32 numElements,
vector<string>& decorations,
vector<string>& pre_mains,
vector<string>& testfuns,
string& operation,
InputWidth inputWidth,
const char* funVariables,
const char* spirvExtension = DE_NULL);
void finalizeFullOperation (string& fullOperation,
const string& resultName,
const bool returnHighPart,
const bool isBooleanResult);
static bool verifyResult (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
deUint32 skip,
tcu::TestLog& log);
static bool verifyDefaultResult (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
tcu::TestLog& log);
static bool verifyVec3Result (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
tcu::TestLog& log);
const char* const m_deviceFeature;
const char* const m_spirvCapability;
const char* const m_spirvType;
InputType m_inputType;
deUint32 m_typeSize;
deUint32 m_vectorSize;
std::string m_spirvTestType;
};
template <class T>
SpvAsmTypeTests<T>::SpvAsmTypeTests (tcu::TestContext& testCtx,
const char* name,
const char* description,
const char* deviceFeature,
const char* spirvCapability,
const char* spirvType,
InputType inputType,
deUint32 typeSize,
deUint32 vectorSize)
: tcu::TestCaseGroup (testCtx, name, description)
, m_rnd (deStringHash(name))
, m_deviceFeature (deviceFeature)
, m_spirvCapability (spirvCapability)
, m_spirvType (spirvType)
, m_inputType (inputType)
, m_typeSize (typeSize)
, m_vectorSize (vectorSize)
{
std::string scalarType;
DE_ASSERT(vectorSize >= 1 && vectorSize <= 4);
if (m_inputType == TYPE_I32)
scalarType = "i32";
else if (m_inputType == TYPE_U32)
scalarType = "u32";
else
scalarType = "";
if (scalarType.empty())
{
m_spirvTestType = UNDEFINED_SPIRV_TEST_TYPE;
}
else
{
if (m_vectorSize > 1)
m_spirvTestType = "v" + de::toString(m_vectorSize) + scalarType;
else
m_spirvTestType = scalarType;
}
}
template <class T>
SpvAsmTypeTests<T>::~SpvAsmTypeTests (void)
{
}
template <class T>
std::string SpvAsmTypeTests<T>::createInputDecoration (deUint32 numInput)
{
const StringTemplate decoration ("OpDecorate %input${n_input} DescriptorSet 0\n"
"OpDecorate %input${n_input} Binding ${n_input}\n");
map<string, string> specs;
specs["n_input"] = de::toString(numInput);
return decoration.specialize(specs);
}
template <class T>
std::string SpvAsmTypeTests<T>::createInputPreMain (deUint32 numInput, deUint32 spirvOpertaion)
{
const bool scalarInput = (m_vectorSize != 1) && isScalarInput(spirvOpertaion, numInput);
const string bufferType = (scalarInput) ? "%scalarbufptr" : "%bufptr";
return "%input" + de::toString(numInput) + " = OpVariable " + bufferType + " Uniform\n";
}
template <class T>
std::string SpvAsmTypeTests<T>::createInputTestfun (deUint32 numInput, deUint32 spirvOpertaion)
{
const bool scalarInput = (m_vectorSize != 1) && isScalarInput(spirvOpertaion, numInput);
const string pointerType = (scalarInput) ? "%up_scalartype" : "%up_testtype";
const string valueType = (scalarInput) ? "%u32" : "%${testtype}";
const StringTemplate testfun ("%input${n_input}_loc = OpAccessChain " + pointerType + " %input${n_input} "
"%c_i32_0 %counter_val\n"
"%input${n_input}_val = OpLoad " + valueType + " %input${n_input}_loc\n");
map<string, string> specs;
specs["n_input"] = de::toString(numInput);
specs["testtype"] = m_spirvTestType;
return testfun.specialize(specs);
}
template <class T>
deUint32 SpvAsmTypeTests<T>::combine (GraphicsResources& resources,
vector<T>& data,
OpUnaryFuncType operation,
UnaryFilterFuncType filter,
InputRange inputRange)
{
DE_UNREF(inputRange);
const deUint32 datasize = static_cast<deUint32>(data.size());
const deUint32 sizeWithPadding = (m_vectorSize == 3) ? 4 : m_vectorSize;
const deUint32 totalPadding = (m_vectorSize == 3) ? (datasize / m_vectorSize) : 0;
const deUint32 total = datasize + totalPadding;
deUint32 padCount = m_vectorSize;
deUint32 outputsSize;
vector<T> inputs;
vector<T> outputs;
inputs.reserve(total);
outputs.reserve(total);
/* According to spec, a three-component vector, with components of size N,
has a base alignment of 4 N */
for (deUint32 elemNdx = 0; elemNdx < datasize; ++elemNdx)
{
if (filter(data[elemNdx]))
{
inputs.push_back(data[elemNdx]);
outputs.push_back(operation(data[elemNdx]));
if (m_vectorSize == 3)
{
padCount--;
if (padCount == 0)
{
inputs.push_back(0);
outputs.push_back(0);
padCount = m_vectorSize;
}
}
}
}
outputsSize = static_cast<deUint32>(outputs.size());
/* Ensure we have pushed a multiple of vector size, including padding if
required */
while (outputsSize % sizeWithPadding != 0)
{
inputs.pop_back();
outputs.pop_back();
outputsSize--;
}
pushResource(resources.inputs, inputs);
pushResource(resources.outputs, outputs);
return outputsSize / sizeWithPadding;
}
template <class T>
deUint32 SpvAsmTypeTests<T>::combine (GraphicsResources& resources,
vector<T>& data,
OpBinaryFuncType operation,
BinaryFilterFuncType filter,
InputRange inputRange)
{
const deUint32 datasize = static_cast<deUint32>(data.size());
const deUint32 sizeWithPadding = (m_vectorSize == 3) ? 4 : m_vectorSize;
const deUint32 totalData = datasize * datasize;
const deUint32 totalPadding = (m_vectorSize == 3) ? (totalData / m_vectorSize) : 0;
const deUint32 total = totalData + totalPadding;
deUint32 padCount = m_vectorSize;
deUint32 outputsSize;
vector<T> inputs0;
vector<T> inputs1;
vector<T> outputs;
inputs0.reserve(total);
inputs1.reserve(total);
outputs.reserve(total);
/* According to spec, a three-component vector, with components of size N,
has a base alignment of 4 N */
for (deUint32 elemNdx1 = 0; elemNdx1 < datasize; ++elemNdx1)
for (deUint32 elemNdx2 = 0; elemNdx2 < datasize; ++elemNdx2)
{
if (filter(data[elemNdx1], data[elemNdx2]))
{
switch (inputRange)
{
case RANGE_FULL:
{
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(data[elemNdx2]);
outputs.push_back(operation(data[elemNdx1], data[elemNdx2]));
break;
}
case RANGE_BIT_WIDTH:
{
// Make sure shift count doesn't exceed the bit width
const T shift = data[elemNdx2] & static_cast<T>(m_typeSize - 1u);
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(shift);
outputs.push_back(operation(data[elemNdx1], shift));
break;
}
default:
DE_FATAL("Not implemented");
}
if (m_vectorSize == 3)
{
padCount--;
if (padCount == 0)
{
inputs0.push_back(0);
inputs1.push_back(0);
outputs.push_back(0);
padCount = m_vectorSize;
}
}
}
}
outputsSize = static_cast<deUint32>(outputs.size());
/* Ensure we have pushed a multiple of vector size, including padding if
required */
while (outputsSize % sizeWithPadding != 0)
{
inputs0.pop_back();
inputs1.pop_back();
outputs.pop_back();
outputsSize--;
}
pushResource(resources.inputs, inputs0);
pushResource(resources.inputs, inputs1);
pushResource(resources.outputs, outputs);
return outputsSize / sizeWithPadding;
}
template <class T>
deUint32 SpvAsmTypeTests<T>::combine (GraphicsResources& resources,
vector<T>& data,
OpTernaryFuncType operation,
TernaryFilterFuncType filter,
InputRange inputRange)
{
const deUint32 datasize = static_cast<deUint32>(data.size());
const deUint32 sizeWithPadding = (m_vectorSize == 3) ? 4 : m_vectorSize;
const deUint32 totalData = datasize * datasize * datasize;
const deUint32 totalPadding = (m_vectorSize == 3) ? (totalData / m_vectorSize) : 0;
const deUint32 total = totalData + totalPadding;
deUint32 padCount = m_vectorSize;
deUint32 outputsSize;
vector<T> inputs0;
vector<T> inputs1;
vector<T> inputs2;
vector<T> outputs;
inputs0.reserve(total);
inputs1.reserve(total);
inputs2.reserve(total);
outputs.reserve(total);
// Reduce the amount of input data in tests without filtering
deUint32 datasize2 = (inputRange == RANGE_BIT_WIDTH_SUM) ? 4u * m_vectorSize : datasize;
T bitOffset = static_cast<T>(0);
T bitCount = static_cast<T>(0);
/* According to spec, a three-component vector, with components of size N,
has a base alignment of 4 N */
for (deUint32 elemNdx1 = 0; elemNdx1 < datasize; ++elemNdx1)
for (deUint32 elemNdx2 = 0; elemNdx2 < datasize2; ++elemNdx2)
for (deUint32 elemNdx3 = 0; elemNdx3 < datasize2; ++elemNdx3)
{
if (filter(data[elemNdx1], data[elemNdx2], data[elemNdx3]))
{
switch (inputRange)
{
case RANGE_FULL:
{
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(data[elemNdx2]);
inputs2.push_back(data[elemNdx3]);
outputs.push_back(operation(data[elemNdx1], data[elemNdx2], data[elemNdx3]));
break;
}
case RANGE_BIT_WIDTH_SUM:
{
if (elemNdx3 % m_vectorSize == 0)
{
bitOffset = static_cast<T>(m_rnd.getUint32() & (m_typeSize - 1u));
bitCount = static_cast<T>(m_rnd.getUint32() & (m_typeSize - 1u));
}
// Make sure the sum of offset and count doesn't exceed bit width
if ((deUint32)(bitOffset + bitCount) > m_typeSize)
bitCount = static_cast<T>(m_typeSize - bitOffset);
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(bitOffset);
inputs2.push_back(bitCount);
outputs.push_back(operation(data[elemNdx1], bitOffset, bitCount));
break;
}
default:
DE_FATAL("Not implemented");
}
if (m_vectorSize == 3)
{
padCount--;
if (padCount == 0)
{
inputs0.push_back(0);
inputs1.push_back(0);
inputs2.push_back(0);
outputs.push_back(0);
padCount = m_vectorSize;
}
}
}
}
outputsSize = static_cast<deUint32>(outputs.size());
/* Ensure we have pushed a multiple of vector size, including padding if
required */
while (outputsSize % sizeWithPadding != 0)
{
inputs0.pop_back();
inputs1.pop_back();
inputs2.pop_back();
outputs.pop_back();
outputsSize--;
}
pushResource(resources.inputs, inputs0);
pushResource(resources.inputs, inputs1);
pushResource(resources.inputs, inputs2);
pushResource(resources.outputs, outputs);
return outputsSize / sizeWithPadding;
}
template <class T>
deUint32 SpvAsmTypeTests<T>::combine (GraphicsResources& resources,
vector<T>& data,
OpQuaternaryFuncType operation,
QuaternaryFilterFuncType filter,
InputRange inputRange)
{
const deUint32 datasize = static_cast<deUint32>(data.size());
const deUint32 sizeWithPadding = (m_vectorSize == 3) ? 4 : m_vectorSize;
const deUint32 totalData = datasize * datasize;
const deUint32 totalPadding = (m_vectorSize == 3) ? (totalData / m_vectorSize) : 0;
const deUint32 total = totalData + totalPadding;
deUint32 padCount = m_vectorSize;
deUint32 outputsSize;
vector<T> inputs0;
vector<T> inputs1;
vector<T> inputs2;
vector<T> inputs3;
vector<T> outputs;
inputs0.reserve(total);
inputs1.reserve(total);
inputs2.reserve(total);
inputs3.reserve(total);
outputs.reserve(total);
// Reduce the amount of input data in tests without filtering
deUint32 datasize2 = (inputRange == RANGE_BIT_WIDTH_SUM) ? 2u * m_vectorSize : datasize;
T bitOffset = static_cast<T>(0);
T bitCount = static_cast<T>(0);
/* According to spec, a three-component vector, with components of size N,
has a base alignment of 4 N */
for (deUint32 elemNdx1 = 0; elemNdx1 < datasize; ++elemNdx1)
for (deUint32 elemNdx2 = 0; elemNdx2 < datasize2; ++elemNdx2)
for (deUint32 elemNdx3 = 0; elemNdx3 < datasize2; ++elemNdx3)
for (deUint32 elemNdx4 = 0; elemNdx4 < datasize2; ++elemNdx4)
{
if (filter(data[elemNdx1], data[elemNdx2], data[elemNdx3], data[elemNdx4]))
{
switch (inputRange)
{
case RANGE_FULL:
{
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(data[elemNdx2]);
inputs2.push_back(data[elemNdx3]);
inputs3.push_back(data[elemNdx3]);
outputs.push_back(operation(data[elemNdx1], data[elemNdx2], data[elemNdx3], data[elemNdx4]));
break;
}
case RANGE_BIT_WIDTH_SUM:
{
if (elemNdx4 % m_vectorSize == 0)
{
bitOffset = static_cast<T>(m_rnd.getUint32() & (m_typeSize - 1u));
bitCount = static_cast<T>(m_rnd.getUint32() & (m_typeSize - 1u));
}
// Make sure the sum of offset and count doesn't exceed bit width
if ((deUint32)(bitOffset + bitCount) > m_typeSize)
bitCount -= bitOffset + bitCount - static_cast<T>(m_typeSize);
inputs0.push_back(data[elemNdx1]);
inputs1.push_back(data[elemNdx2]);
inputs2.push_back(bitOffset);
inputs3.push_back(bitCount);
outputs.push_back(operation(data[elemNdx1], data[elemNdx2], bitOffset, bitCount));
break;
}
default:
DE_FATAL("Not implemented");
}
if (m_vectorSize == 3)
{
padCount--;
if (padCount == 0)
{
inputs0.push_back(0);
inputs1.push_back(0);
inputs2.push_back(0);
inputs3.push_back(0);
outputs.push_back(0);
padCount = m_vectorSize;
}
}
}
}
outputsSize = static_cast<deUint32>(outputs.size());
/* Ensure we have pushed a multiple of vector size, including padding if
required */
while (outputsSize % sizeWithPadding != 0)
{
inputs0.pop_back();
inputs1.pop_back();
inputs2.pop_back();
inputs3.pop_back();
outputs.pop_back();
outputsSize--;
}
pushResource(resources.inputs, inputs0);
pushResource(resources.inputs, inputs1);
pushResource(resources.inputs, inputs2);
pushResource(resources.inputs, inputs3);
pushResource(resources.outputs, outputs);
return outputsSize / sizeWithPadding;
}
template <class T>
deUint32 SpvAsmTypeTests<T>::fillResources (GraphicsResources& resources,
vector<T>& data)
{
vector<T> outputs;
outputs.reserve(data.size());
for (deUint32 elemNdx = 0; elemNdx < data.size(); ++elemNdx)
{
if (data[elemNdx] == m_cases[0])
outputs.push_back(100);
else if (data[elemNdx] == m_cases[1])
outputs.push_back(110);
else if (data[elemNdx] == m_cases[2])
outputs.push_back(120);
else
outputs.push_back(10);
}
pushResource(resources.inputs, data);
pushResource(resources.inputs, outputs);
return static_cast<deUint32>(outputs.size());
}
template <class T>
void SpvAsmTypeTests<T>::createStageTests (const char* testName,
GraphicsResources& resources,
deUint32 numElements,
vector<string>& decorations,
vector<string>& pre_mains,
vector<string>& testfuns,
string& operation,
InputWidth inputWidth,
const char* funVariables,
const char* spirvExtension)
{
const StringTemplate decoration ("OpDecorate %output DescriptorSet 0\n"
"OpDecorate %output Binding ${output_binding}\n"
"OpDecorate %a${num_elements}testtype ArrayStride ${typesize}\n"
"OpDecorate %buf BufferBlock\n"
"OpMemberDecorate %buf 0 Offset 0\n");
const StringTemplate vecDecoration ("OpDecorate %a${num_elements}scalartype ArrayStride ${typesize}\n"
"OpDecorate %scalarbuf BufferBlock\n"
"OpMemberDecorate %scalarbuf 0 Offset 0\n");
const StringTemplate pre_pre_main ("%c_u32_${num_elements} = OpConstant %u32 ${num_elements}\n"
"%c_i32_${num_elements} = OpConstant %i32 ${num_elements}\n");
const StringTemplate scalar_pre_main ("%testtype = ${scalartype}\n");
const StringTemplate vector_pre_main ("%scalartype = ${scalartype}\n"
"%testtype = OpTypeVector %scalartype ${vector_size}\n");
const StringTemplate pre_main_consts ("%c_shift = OpConstant %u32 16\n"
"${constant_zero}\n"
"${constant_one}\n");
const StringTemplate pre_main_constv ("%c_shift1 = OpConstant %u32 16\n"
"%c_shift = OpConstantComposite %v${vector_size}u32 ${shift_initializers}\n"
"${bvec}\n"
"${constant_zero}\n"
"${constant_one}\n"
"%a${num_elements}scalartype = OpTypeArray %u32 %c_u32_${num_elements}\n"
"%up_scalartype = OpTypePointer Uniform %u32\n"
"%scalarbuf = OpTypeStruct %a${num_elements}scalartype\n"
"%scalarbufptr = OpTypePointer Uniform %scalarbuf\n");
const StringTemplate post_pre_main ("%a${num_elements}testtype = OpTypeArray %${testtype} "
"%c_u32_${num_elements}\n"
"%up_testtype = OpTypePointer Uniform %${testtype}\n"
"%buf = OpTypeStruct %a${num_elements}testtype\n"
"%bufptr = OpTypePointer Uniform %buf\n"
"%output = OpVariable %bufptr Uniform\n"
"${other_size_types}\n"
"${u32_function_pointer}\n");
const StringTemplate pre_testfun ("%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
"%param = OpFunctionParameter %v4f32\n"
"%entry = OpLabel\n"
"%op_constant = OpVariable %fp_${testtype} Function\n"
+ string(funVariables) +
"%counter = OpVariable %fp_i32 Function\n"
"OpStore %counter %c_i32_0\n"
"OpBranch %loop\n"
"%loop = OpLabel\n"
"%counter_val = OpLoad %i32 %counter\n"
"%lt = OpSLessThan %bool %counter_val %c_i32_${num_elements}\n"
"OpLoopMerge %exit %inc None\n"
"OpBranchConditional %lt %write %exit\n"
"%write = OpLabel\n"
"%output_loc = OpAccessChain %up_testtype %output %c_i32_0 "
"%counter_val\n");
const StringTemplate post_testfun ("OpStore %output_loc %op_result\n"
"OpBranch %inc\n"
"%inc = OpLabel\n"
"%counter_val_next = OpIAdd %i32 %counter_val %c_i32_1\n"
"OpStore %counter %counter_val_next\n"
"OpBranch %loop\n"
"%exit = OpLabel\n"
"OpReturnValue %param\n"
"OpFunctionEnd\n");
const bool uses8bit (m_inputType == TYPE_I8 || m_inputType == TYPE_U8 || has8BitInputWidth(inputWidth));
const string vectorSizeStr (de::toString(m_vectorSize));
std::vector<std::string> noExtensions;
std::vector<std::string> features;
RGBA defaultColors[4];
map<string, string> fragments;
map<string, string> specs;
VulkanFeatures requiredFeatures;
std::string spirvExtensions;
std::string spirvCapabilities;
getDefaultColors(defaultColors);
if (m_vectorSize == 3)
resources.verifyIO = verifyVec3Result;
else
resources.verifyIO = verifyDefaultResult;
// All of the following tests write their results into an output SSBO, therefore they require the following features.
requiredFeatures.coreFeatures.vertexPipelineStoresAndAtomics = DE_TRUE;
requiredFeatures.coreFeatures.fragmentStoresAndAtomics = DE_TRUE;
if (m_deviceFeature)
features.insert(features.begin(), m_deviceFeature);
if (inputWidth != WIDTH_DEFAULT)
{
if (has16BitInputWidth(inputWidth))
features.insert(features.begin(), "shaderInt16");
if (has64BitInputWidth(inputWidth))
features.insert(features.begin(), "shaderInt64");
}
if (uses8bit)
{
requiredFeatures.extFloat16Int8 |= EXTFLOAT16INT8FEATURES_INT8;
}
if (m_inputType == TYPE_I8 || m_inputType == TYPE_U8)
{
requiredFeatures.ext8BitStorage |= EXT8BITSTORAGEFEATURES_UNIFORM_STORAGE_BUFFER;
spirvExtensions += "OpExtension \"SPV_KHR_8bit_storage\"\n";
}
if (m_inputType == TYPE_I16 || m_inputType == TYPE_U16)
{
requiredFeatures.ext16BitStorage |= EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK;
spirvExtensions += "OpExtension \"SPV_KHR_16bit_storage\"\n";
}
specs["testtype"] = m_spirvTestType;
specs["scalartype"] = m_spirvType;
specs["typesize"] = de::toString(((m_vectorSize == 3) ? 4 : m_vectorSize) * m_typeSize / 8);
specs["vector_size"] = vectorSizeStr;
specs["num_elements"] = de::toString(numElements);
specs["output_binding"] = de::toString(resources.inputs.size());
specs["shift_initializers"] = replicate(" %c_shift1", m_vectorSize);
specs["bvec"] = (m_vectorSize == 1 || m_vectorSize == 4) ? ("")
: ("%v" + vectorSizeStr + "bool = OpTypeVector %bool " + vectorSizeStr);
specs["constant_zero"] = (m_vectorSize == 1)
? ("%c_zero = OpConstant %u32 0\n")
: ("%c_zero = OpConstantComposite %v" + vectorSizeStr + "u32" + replicate(" %c_u32_0", m_vectorSize));
specs["constant_one"] = (m_vectorSize == 1)
? ("%c_one = OpConstant %u32 1\n")
: ("%c_one = OpConstantComposite %v" + vectorSizeStr + "u32" + replicate(" %c_u32_1", m_vectorSize));
specs["other_size_types"] = (inputWidth == WIDTH_DEFAULT) ? ("")
: getOtherSizeTypes(m_inputType, m_vectorSize, inputWidth);
specs["u32_function_pointer"] = deStringEqual(m_spirvTestType.c_str(), "i32") ? ("")
: ("%fp_" + m_spirvTestType + " = OpTypePointer Function %" + m_spirvTestType + "\n");
if (spirvExtension)
spirvExtensions += "%ext1 = OpExtInstImport \"" + string(spirvExtension) + "\"";
for (deUint32 elemNdx = 0; elemNdx < decorations.size(); ++elemNdx)
fragments["decoration"] += decorations[elemNdx];
fragments["decoration"] += decoration.specialize(specs);
if (m_vectorSize > 1)
fragments["decoration"] += vecDecoration.specialize(specs);
fragments["pre_main"] = pre_pre_main.specialize(specs);
if (specs["testtype"].compare(UNDEFINED_SPIRV_TEST_TYPE) == 0)
{
if (m_vectorSize > 1)
fragments["pre_main"] += vector_pre_main.specialize(specs);
else
fragments["pre_main"] += scalar_pre_main.specialize(specs);
}
if (m_vectorSize > 1)
fragments["pre_main"] += pre_main_constv.specialize(specs);
else
fragments["pre_main"] += pre_main_consts.specialize(specs);
fragments["pre_main"] += post_pre_main.specialize(specs);
for (deUint32 elemNdx = 0; elemNdx < pre_mains.size(); ++elemNdx)
fragments["pre_main"] += pre_mains[elemNdx];
fragments["testfun"] = pre_testfun.specialize(specs);
for (deUint32 elemNdx = 0; elemNdx < testfuns.size(); ++elemNdx)
fragments["testfun"] += testfuns[elemNdx];
fragments["testfun"] += operation + post_testfun.specialize(specs);
spirvCapabilities += getSpirvCapabilityStr(m_spirvCapability, inputWidth);
fragments["extension"] = spirvExtensions;
fragments["capability"] = spirvCapabilities;
requiredFeaturesFromStrings(features, requiredFeatures);
createTestsForAllStages(testName, defaultColors, defaultColors, fragments, resources, noExtensions, this, requiredFeatures);
}
template <class T>
std::string valueToStr(const T v)
{
std::stringstream s;
s << v;
return s.str();
}
template <>
std::string valueToStr<deUint8> (const deUint8 v)
{
std::stringstream s;
s << (deUint16)v;
return s.str();
}
template <>
std::string valueToStr<deInt8> ( const deInt8 v)
{
std::stringstream s;
s << (deInt16)v;
return s.str();
}
template <class T>
bool SpvAsmTypeTests<T>::verifyResult (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
deUint32 skip,
tcu::TestLog& log)
{
DE_ASSERT(outputAllocations.size() == 1);
DE_ASSERT(inputs.size() > 0 && inputs.size() < 5);
const T* input[4] = { DE_NULL };
vector<deUint8> inputBytes[4];
vector<deUint8> expectedBytes;
expectedOutputs[0].getBytes(expectedBytes);
const deUint32 count = static_cast<deUint32>(expectedBytes.size() / sizeof(T));
const T* obtained = static_cast<const T *>(outputAllocations[0]->getHostPtr());
const T* expected = reinterpret_cast<const T*>(&expectedBytes.front());
for (deUint32 ndxCount = 0; ndxCount < inputs.size(); ndxCount++)
{
inputs[ndxCount].getBytes(inputBytes[ndxCount]);
input[ndxCount] = reinterpret_cast<const T*>(&inputBytes[ndxCount].front());
}
for (deUint32 ndxCount = 0 ; ndxCount < count; ++ndxCount)
{
/* Skip padding */
if (((ndxCount + 1) % skip) == 0)
continue;
if (obtained[ndxCount] != expected[ndxCount])
{
std::stringstream inputStream;
inputStream << "(";
for (deUint32 ndxIndex = 0 ; ndxIndex < inputs.size(); ++ndxIndex)
{
inputStream << valueToStr(input[ndxIndex][ndxCount]);
if (ndxIndex < inputs.size() - 1)
inputStream << ",";
}
inputStream << ")";
log << tcu::TestLog::Message
<< "Error: found unexpected result for inputs " << inputStream.str()
<< ": expected " << valueToStr(expected[ndxCount]) << ", obtained "
<< valueToStr(obtained[ndxCount]) << tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::verifyDefaultResult (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
tcu::TestLog& log)
{
return verifyResult(inputs, outputAllocations, expectedOutputs, ~0, log);
}
template <class T>
bool SpvAsmTypeTests<T>::verifyVec3Result (const vector<Resource>& inputs,
const vector<AllocationSp>& outputAllocations,
const vector<Resource>& expectedOutputs,
tcu::TestLog& log)
{
return verifyResult(inputs, outputAllocations, expectedOutputs, 4, log);
}
template <class T>
string SpvAsmTypeTests<T>::createConstantDeclaration (vector<T>& dataset, deUint32 spirvOperation)
{
const bool isVariableTest = (SpvOpVariable == spirvOperation);
const bool isConstantNullTest = (SpvOpConstantNull == spirvOperation) || isVariableTest;
const bool isConstantCompositeTest = (SpvOpConstantComposite == spirvOperation) || (isConstantNullTest && m_vectorSize > 1);
const bool isConstantTest = (SpvOpConstant == spirvOperation) || isConstantCompositeTest || isConstantNullTest;
const bool isSpecConstantTest = (SpvOpSpecConstant == spirvOperation);
const bool isSpecConstantCompositeTest = (SpvOpSpecConstantComposite == spirvOperation);
const string testScalarType = (m_inputType == TYPE_I32) ? "i32"
: (m_inputType == TYPE_U32) ? "u32"
: "scalartype";
const string constantType = (m_vectorSize > 1) ? testScalarType : m_spirvTestType;
const string constantName = (m_vectorSize > 1) ? "%c_constituent_" : "%c_testtype_";
string str = "";
// Declare scalar specialization constants
if (isSpecConstantTest)
{
for (size_t constantNdx = 0u; constantNdx < dataset.size(); constantNdx++)
str += constantName + de::toString(constantNdx) + " = OpSpecConstant %" + constantType + " " + de::toString(dataset[constantNdx]) + "\n";
}
// Declare specialization constant composites
if (isSpecConstantCompositeTest)
{
// Constituents are a mix of OpConstantNull, OpConstants and OpSpecializationConstants
for (size_t constantNdx = 0u; constantNdx < dataset.size(); constantNdx++)
{
const char* constantOp[] =
{
"OpConstant",
"OpSpecConstant"
};
if (constantNdx == 0u)
str += constantName + de::toString(constantNdx) + " = OpConstantNull %" + constantType + "\n";
else
str += constantName + de::toString(constantNdx) + " = " + constantOp[constantNdx % 2] + " %" + constantType + " " + de::toString(dataset[constantNdx]) + "\n";
}
for (deUint32 compositeNdx = 0u; compositeNdx < (deUint32)dataset.size(); compositeNdx++)
{
str += "%c_testtype_" + de::toString(compositeNdx) + " = OpSpecConstantComposite %" + m_spirvTestType;
for (deUint32 componentNdx = 0u; componentNdx < m_vectorSize; componentNdx++)
str += " %c_constituent_" + de::toString(getConstituentIndex(compositeNdx * m_vectorSize + componentNdx, m_vectorSize));
str += "\n";
}
}
// Declare scalar constants
if (isConstantTest || isVariableTest)
{
for (size_t constantNdx = 0u; constantNdx < dataset.size(); constantNdx++)
{
if (isConstantNullTest && constantNdx == 0u)
str += constantName + de::toString(constantNdx) + " = OpConstantNull %" + constantType + "\n";
else
str += constantName + de::toString(constantNdx) + " = OpConstant %" + constantType + " " + de::toString(dataset[constantNdx]) + "\n";
}
}
// Declare constant composites
if (isConstantCompositeTest)
{
for (deUint32 compositeNdx = 0u; compositeNdx < (deUint32)dataset.size(); compositeNdx++)
{
str += "%c_testtype_" + de::toString(compositeNdx) + " = OpConstantComposite %" + m_spirvTestType;
for (deUint32 componentNdx = 0u; componentNdx < m_vectorSize; componentNdx++)
str += " %c_constituent_" + de::toString(getConstituentIndex(compositeNdx * m_vectorSize + componentNdx, m_vectorSize));
str += "\n";
}
}
return str;
}
template <class T>
string getVariableStr (vector<T>& dataset, const char* spirvType, deUint32 spirvOperation)
{
const bool isVariableTest = (SpvOpVariable == spirvOperation);
string str = "";
// Declare variables with initializers
if (isVariableTest)
for (size_t i = 0u; i < dataset.size(); i++)
str += "%testvariable_" + de::toString(i) + " = OpVariable %fp_" + spirvType + " Function %c_testtype_" + de::toString(i) + "\n";
return str;
}
template <class T>
void SpvAsmTypeTests<T>::createTests (const char* testName,
deUint32 spirvOperation,
OpUnaryFuncType operation,
UnaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart)
{
DE_ASSERT(!isBooleanResultTest(spirvOperation));
const string resultName = returnHighPart ? "%op_result_pre" : "%op_result";
OpUnaryFuncType zeroFunc = &zero;
vector<T> dataset;
vector<string> decorations;
vector<string> pre_mains;
vector<string> testfuns;
GraphicsResources resources;
map<string, string> fragments;
map<string, string> specs;
if (isConstantOrVariableTest(spirvOperation))
{
DE_ASSERT(!spirvExtension);
const deUint32 inputSize = TEST_DATASET_SIZE;
const deUint32 outputSize = TEST_DATASET_SIZE * m_vectorSize;
vector<T> inputDataset;
inputDataset.reserve(inputSize);
dataset.reserve(outputSize);
getDataset(inputDataset, inputSize);
getConstantDataset(inputDataset, dataset, spirvOperation);
const deUint32 totalElements = combine(resources, dataset, (returnHighPart ? zeroFunc : operation), filter, inputRange);
pre_mains.reserve(1);
pre_mains.push_back(createConstantDeclaration(inputDataset, spirvOperation));
string fullOperation = "OpBranch %switchStart\n"
"%switchStart = OpLabel\n"
"OpSelectionMerge %switchEnd None\n"
"OpSwitch %counter_val %caseDefault";
for (deUint32 caseNdx = 0u; caseNdx < inputSize; caseNdx++)
fullOperation += " " + de::toString(caseNdx) + " " + "%case" + de::toString(caseNdx);
fullOperation += "\n";
const string funVariables = getVariableStr(inputDataset, m_spirvTestType.c_str(), spirvOperation);
if (SpvOpVariable == spirvOperation)
{
for (deUint32 caseNdx = 0u; caseNdx < inputSize; caseNdx++)
fullOperation += "%case" + de::toString(caseNdx) + " = OpLabel\n"
"%temp_" + de::toString(caseNdx) + " = OpLoad %" + m_spirvTestType + " %testvariable_" + de::toString(caseNdx) + "\n"
"OpStore %op_constant %temp_" + de::toString(caseNdx) + "\n"
"OpBranch %switchEnd\n";
}
else
{
for (deUint32 caseNdx = 0u; caseNdx < inputSize; caseNdx++)
fullOperation += "%case" + de::toString(caseNdx) + " = OpLabel\n"
"OpStore %op_constant %c_testtype_" + de::toString(caseNdx) + "\n"
"OpBranch %switchEnd\n";
}
fullOperation += "%caseDefault = OpLabel\n"
"OpBranch %switchEnd\n"
"%switchEnd = OpLabel\n"
+ resultName + " = OpLoad %" + m_spirvTestType + " %op_constant\n";
finalizeFullOperation(fullOperation, resultName, returnHighPart, false);
createStageTests(testName, resources, totalElements, decorations,
pre_mains, testfuns, fullOperation, inputWidth, funVariables.c_str(), spirvExtension);
}
else
{
dataset.reserve(TEST_DATASET_SIZE * m_vectorSize);
getDataset(dataset, TEST_DATASET_SIZE * m_vectorSize);
const deUint32 totalElements = combine(resources, dataset, (returnHighPart ? zeroFunc : operation), filter, inputRange);
decorations.reserve(1);
pre_mains.reserve(1);
testfuns.reserve(1);
decorations.push_back(createInputDecoration(0));
pre_mains.push_back(createInputPreMain(0, spirvOperation));
testfuns.push_back(createInputTestfun(0, spirvOperation));
string full_operation (spirvExtension ? resultName + " = OpExtInst %" + m_spirvTestType + " %ext1 " + getGLSLstd450OperationStr(spirvOperation) + " %input0_val\n"
: resultName + " = " + getSpvOperationStr(spirvOperation) + " %" + m_spirvTestType + " %input0_val\n");
finalizeFullOperation(full_operation, resultName, returnHighPart, false);
createStageTests(testName, resources, totalElements, decorations,
pre_mains, testfuns, full_operation, inputWidth, "", spirvExtension);
}
}
template <class T>
void SpvAsmTypeTests<T>::createTests (const char* testName,
deUint32 spirvOperation,
OpBinaryFuncType operation,
BinaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart)
{
const bool isBoolean = isBooleanResultTest(spirvOperation);
const string resultName = (returnHighPart || isBoolean) ? "%op_result_pre" : "%op_result";
const string resultType = isBoolean ? getBooleanResultType(m_vectorSize) : m_spirvTestType;
OpBinaryFuncType zeroFunc = &zero;
vector<T> dataset;
vector<string> decorations;
vector<string> pre_mains;
vector<string> testfuns;
GraphicsResources resources;
map<string, string> fragments;
map<string, string> specs;
string full_operation;
dataset.reserve(TEST_DATASET_SIZE * m_vectorSize);
getDataset(dataset, TEST_DATASET_SIZE * m_vectorSize);
const deUint32 totalElements = combine(resources, dataset, (returnHighPart ? zeroFunc : operation), filter, inputRange);
decorations.reserve(2);
pre_mains.reserve(2);
testfuns.reserve(2);
for (deUint32 elemNdx = 0; elemNdx < 2; ++elemNdx)
{
decorations.push_back(createInputDecoration(elemNdx));
pre_mains.push_back(createInputPreMain(elemNdx, spirvOperation));
testfuns.push_back(createInputTestfun(elemNdx, spirvOperation));
}
if (spirvOperation != DE_NULL)
{
if (inputWidth == WIDTH_DEFAULT)
full_operation = spirvExtension ? resultName + " = OpExtInst %" + resultType + " %ext1 " + getGLSLstd450OperationStr(spirvOperation) + " %input0_val %input1_val\n"
: resultName + " = " + getSpvOperationStr(spirvOperation) + " %" + resultType + " %input0_val %input1_val\n";
else
full_operation = getBinaryFullOperationWithInputWidthStr(resultName, getSpvOperationStr(spirvOperation), m_inputType, m_spirvTestType, m_vectorSize, inputWidth);
}
else
{
if (deStringBeginsWith(testName, "mul_sdiv"))
{
DE_ASSERT(spirvExtension == DE_NULL);
full_operation = "%op_result2 = OpIMul %" + m_spirvTestType + " %input0_val %input1_val\n";
full_operation += resultName + " = OpSDiv %" + m_spirvTestType + " %op_result2 %input1_val\n";
}
if (deStringBeginsWith(testName, "mul_udiv"))
{
DE_ASSERT(spirvExtension == DE_NULL);
full_operation = "%op_result2 = OpIMul %" + m_spirvTestType + " %input0_val %input1_val\n";
full_operation += resultName + " = OpUDiv %" + m_spirvTestType + " %op_result2 %input1_val\n";
}
}
finalizeFullOperation(full_operation, resultName, returnHighPart, isBoolean);
createStageTests(testName, resources, totalElements, decorations,
pre_mains, testfuns, full_operation, inputWidth, "", spirvExtension);
}
template <class T>
void SpvAsmTypeTests<T>::createTests (const char* testName,
deUint32 spirvOperation,
OpTernaryFuncType operation,
TernaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart)
{
DE_ASSERT(!isBooleanResultTest(spirvOperation));
const string resultName = returnHighPart ? "%op_result_pre" : "%op_result";
OpTernaryFuncType zeroFunc = &zero;
vector<T> dataset;
vector<string> decorations;
vector<string> pre_mains;
vector<string> testfuns;
GraphicsResources resources;
map<string, string> fragments;
map<string, string> specs;
dataset.reserve(TEST_DATASET_SIZE * m_vectorSize);
getDataset(dataset, TEST_DATASET_SIZE * m_vectorSize);
const deUint32 totalElements = combine(resources, dataset, (returnHighPart ? zeroFunc : operation), filter, inputRange);
decorations.reserve(3);
pre_mains.reserve(3);
testfuns.reserve(3);
for (deUint32 elemNdx = 0; elemNdx < 3; ++elemNdx)
{
decorations.push_back(createInputDecoration(elemNdx));
pre_mains.push_back(createInputPreMain(elemNdx, spirvOperation));
testfuns.push_back(createInputTestfun(elemNdx, spirvOperation));
}
string full_operation = "";
if (inputWidth == WIDTH_DEFAULT)
full_operation = (spirvExtension ? resultName + " = OpExtInst %" + m_spirvTestType + " %ext1 " + getGLSLstd450OperationStr(spirvOperation) + " %input0_val %input1_val %input2_val\n"
: resultName + " = " + getSpvOperationStr(spirvOperation) + " %" + m_spirvTestType + " %input0_val %input1_val %input2_val\n");
else
full_operation = getFullOperationWithDifferentInputWidthStr(resultName, getSpvOperationStr(spirvOperation), m_inputType, m_spirvTestType, inputWidth, false);
finalizeFullOperation(full_operation, resultName, returnHighPart, false);
createStageTests(testName, resources, totalElements, decorations,
pre_mains, testfuns, full_operation, inputWidth, "", spirvExtension);
}
template <class T>
void SpvAsmTypeTests<T>::createTests (const char* testName,
deUint32 spirvOperation,
OpQuaternaryFuncType operation,
QuaternaryFilterFuncType filter,
InputRange inputRange,
InputWidth inputWidth,
const char* spirvExtension,
const bool returnHighPart)
{
DE_ASSERT(!spirvExtension);
DE_ASSERT(!isBooleanResultTest(spirvOperation));
const string resultName = returnHighPart ? "%op_result_pre" : "%op_result";
OpQuaternaryFuncType zeroFunc = &zero;
vector<T> dataset;
vector<string> decorations;
vector<string> pre_mains;
vector<string> testfuns;
GraphicsResources resources;
map<string, string> fragments;
map<string, string> specs;
string full_operation;
dataset.reserve(TEST_DATASET_SIZE * m_vectorSize);
getDataset(dataset, TEST_DATASET_SIZE * m_vectorSize);
const deUint32 totalElements = combine(resources, dataset, (returnHighPart ? zeroFunc : operation), filter, inputRange);
decorations.reserve(4);
pre_mains.reserve(4);
testfuns.reserve(4);
for (deUint32 elemNdx = 0; elemNdx < 4; ++elemNdx)
{
decorations.push_back(createInputDecoration(elemNdx));
pre_mains.push_back(createInputPreMain(elemNdx, spirvOperation));
testfuns.push_back(createInputTestfun(elemNdx, spirvOperation));
}
if (inputWidth == WIDTH_DEFAULT)
full_operation = resultName + " = " + getSpvOperationStr(spirvOperation) + " %" + m_spirvTestType + " %input0_val %input1_val %input2_val %input3_val\n";
else
full_operation = getFullOperationWithDifferentInputWidthStr(resultName, getSpvOperationStr(spirvOperation), m_inputType, m_spirvTestType, inputWidth, true);
finalizeFullOperation(full_operation, resultName, returnHighPart, false);
createStageTests(testName, resources, totalElements, decorations,
pre_mains, testfuns, full_operation, inputWidth, "", spirvExtension);
}
template <class T>
void SpvAsmTypeTests<T>::createSwitchTests (void)
{
const StringTemplate decoration ("OpDecorate %input DescriptorSet 0\n"
"OpDecorate %input Binding 0\n"
"OpDecorate %input NonWritable\n"
"OpDecorate %expectedOutput DescriptorSet 0\n"
"OpDecorate %expectedOutput Binding 1\n"
"OpDecorate %expectedOutput NonWritable\n"
"OpDecorate %a${num_elements}testtype ArrayStride ${typesize}\n"
"OpDecorate %buf BufferBlock\n"
"OpMemberDecorate %buf 0 Offset 0\n");
const StringTemplate pre_pre_main ("%fp_bool = OpTypePointer Function %bool\n"
"%c_u32_${num_elements} = OpConstant %u32 ${num_elements}\n"
"%c_i32_${num_elements} = OpConstant %i32 ${num_elements}\n");
const StringTemplate scalar_pre_main ("%testtype = ${scalartype}\n");
const StringTemplate post_pre_main ("%c_casedefault = OpConstant %${testtype} 10\n"
"%c_case0 = OpConstant %${testtype} 100\n"
"%c_case1 = OpConstant %${testtype} 110\n"
"%c_case2 = OpConstant %${testtype} 120\n"
"%a${num_elements}testtype = OpTypeArray %${testtype} %c_u32_${num_elements}\n"
"%up_testtype = OpTypePointer Uniform %${testtype}\n"
"%buf = OpTypeStruct %a${num_elements}testtype\n"
"%bufptr = OpTypePointer Uniform %buf\n"
"%input = OpVariable %bufptr Uniform\n"
"%expectedOutput = OpVariable %bufptr Uniform\n");
const StringTemplate testfun ("%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
"%param = OpFunctionParameter %v4f32\n"
"%entry = OpLabel\n"
"%counter = OpVariable %fp_i32 Function\n"
"%return = OpVariable %fp_v4f32 Function\n"
"%works = OpVariable %fp_bool Function\n"
"OpStore %counter %c_i32_0\n"
"OpStore %return %param\n"
"OpBranch %loop\n"
"%loop = OpLabel\n"
"%counter_val = OpLoad %i32 %counter\n"
"%lt = OpSLessThan %bool %counter_val %c_i32_${num_elements}\n"
"OpLoopMerge %loop_exit %inc None\n"
"OpBranchConditional %lt %load %loop_exit\n"
"%load = OpLabel\n"
"%input_loc = OpAccessChain %up_testtype %input %c_i32_0 %counter_val\n"
"%input_val = OpLoad %${testtype} %input_loc\n"
"%expectedOutput_loc = OpAccessChain %up_testtype %expectedOutput %c_i32_0 %counter_val\n"
"%expectedOutput_val = OpLoad %${testtype} %expectedOutput_loc\n"
"OpSelectionMerge %switch_exit None\n"
"OpSwitch %input_val %default ${case0} %case0 ${case1} %case1 ${case2} %case2\n"
"%default = OpLabel\n"
"%is_default = OpIEqual %bool %expectedOutput_val %c_casedefault\n"
"OpBranch %switch_exit\n"
"%case0 = OpLabel\n"
"%is_case0 = OpIEqual %bool %expectedOutput_val %c_case0\n"
"OpBranch %switch_exit\n"
"%case1 = OpLabel\n"
"%is_case1 = OpIEqual %bool %expectedOutput_val %c_case1\n"
"OpBranch %switch_exit\n"
"%case2 = OpLabel\n"
"%is_case2 = OpIEqual %bool %expectedOutput_val %c_case2\n"
"OpBranch %switch_exit\n"
"%switch_exit = OpLabel\n"
"%case_result = OpPhi %bool %is_default %default %is_case0 %case0 %is_case1 %case1 %is_case2 %case2\n"
"OpSelectionMerge %result_end None\n"
"OpBranchConditional %case_result %result_correct %result_incorrect\n"
"%result_correct = OpLabel\n"
"OpBranch %result_end\n"
"%result_incorrect = OpLabel\n"
"%counter_val_end = OpIAdd %i32 %counter_val %c_i32_${num_elements}\n"
"OpStore %counter %counter_val_end\n"
"OpStore %return %c_v4f32_1_0_0_1\n"
"OpBranch %result_end\n"
"%result_end = OpLabel\n"
"OpBranch %inc\n"
"%inc = OpLabel\n"
"%counter_val_next = OpIAdd %i32 %counter_val %c_i32_1\n"
"OpStore %counter %counter_val_next\n"
"OpBranch %loop\n"
"%loop_exit = OpLabel\n"
"%return_val = OpLoad %v4f32 %return\n"
"OpReturnValue %return_val\n"
"OpFunctionEnd\n");
const bool uses8bit (m_inputType == TYPE_I8 || m_inputType == TYPE_U8);
GraphicsResources resources;
RGBA defaultColors[4];
map<string, string> fragments;
map<string, string> specs;
std::vector<string> noExtensions;
std::vector<string> features;
VulkanFeatures requiredFeatures;
vector<T> dataset;
deUint32 numElements;
std::string spirvExtensions;
std::string spirvCapabilities;
getDefaultColors(defaultColors);
dataset.reserve(TEST_DATASET_SIZE);
getDataset(dataset, TEST_DATASET_SIZE);
numElements = fillResources(resources, dataset);
if (m_deviceFeature)
features.insert(features.begin(), m_deviceFeature);
if (uses8bit)
{
requiredFeatures.extFloat16Int8 |= EXTFLOAT16INT8FEATURES_INT8;
}
if (m_inputType == TYPE_I8 || m_inputType == TYPE_U8)
{
requiredFeatures.ext8BitStorage |= EXT8BITSTORAGEFEATURES_UNIFORM_STORAGE_BUFFER;
spirvExtensions += "OpExtension \"SPV_KHR_8bit_storage\"\n";
}
if (m_inputType == TYPE_I16 || m_inputType == TYPE_U16)
{
requiredFeatures.ext16BitStorage |= EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK;
spirvExtensions += "OpExtension \"SPV_KHR_16bit_storage\"\n";
}
specs["testtype"] = m_spirvTestType;
specs["scalartype"] = m_spirvType;
specs["typesize"] = de::toString(m_typeSize / 8);
specs["num_elements"] = de::toString(numElements);
specs["case0"] = de::toString(m_cases[0]);
specs["case1"] = de::toString(m_cases[1]);
specs["case2"] = de::toString(m_cases[2]);
fragments["decoration"] = decoration.specialize(specs);
fragments["pre_main"] = pre_pre_main.specialize(specs);
if (specs["testtype"].compare(UNDEFINED_SPIRV_TEST_TYPE) == 0)
fragments["pre_main"] += scalar_pre_main.specialize(specs);
fragments["pre_main"] += post_pre_main.specialize(specs);
fragments["testfun"] = testfun.specialize(specs);
spirvCapabilities += getSpirvCapabilityStr(m_spirvCapability, WIDTH_DEFAULT);
fragments["extension"] = spirvExtensions;
fragments["capability"] = spirvCapabilities;
requiredFeaturesFromStrings(features, requiredFeatures);
createTestsForAllStages("switch", defaultColors, defaultColors, fragments, resources, noExtensions, this, requiredFeatures);
}
template <class T>
void SpvAsmTypeTests<T>::getConstantDataset (vector<T> inputDataset, vector<T>& outputDataset, deUint32 spirvOperation)
{
const deUint32 numElements = (deUint32)inputDataset.size();
if ((SpvOpConstant == spirvOperation) || (SpvOpSpecConstant == spirvOperation))
{
for (deUint32 elementNdx = 0u; elementNdx < numElements; elementNdx++)
outputDataset.push_back(inputDataset[elementNdx]);
}
else
{
for (deUint32 elementNdx = 0; elementNdx < numElements * m_vectorSize; elementNdx++)
outputDataset.push_back(inputDataset[getConstituentIndex(elementNdx, m_vectorSize)]);
}
}
template <class T>
void SpvAsmTypeTests<T>::finalizeFullOperation (string& fullOperation,
const string& resultName,
const bool returnHighPart,
const bool isBooleanResult)
{
DE_ASSERT(!fullOperation.empty());
if (returnHighPart)
{
DE_ASSERT(sizeof(T) == sizeof(deInt16));
DE_ASSERT((m_inputType == TYPE_I16) || (m_inputType == TYPE_U16));
const bool signedness = (m_inputType == TYPE_I16);
const string convertOp = signedness ? "OpSConvert" : "OpUConvert";
const string convertPrefix = (m_vectorSize == 1) ? "" : "v" + de::toString(m_vectorSize);
const string convertType = convertPrefix + "u32";
// Zero extend value to double-width value, then return high part
fullOperation += "%op_result_a = OpUConvert %" + convertType + " " + resultName + "\n";
fullOperation += "%op_result_b = OpShiftRightLogical %" + convertType + " %op_result_a %c_shift\n";
fullOperation += "%op_result = " + convertOp + " %" + m_spirvTestType + " %op_result_b\n";
}
else if (isBooleanResult)
{
const string selectType = (m_vectorSize == 1) ? ("u32") : ("v" + de::toString(m_vectorSize) + "u32");
// Convert boolean values to result format
if (m_inputType == TYPE_U32)
{
fullOperation += "%op_result = OpSelect %" + selectType + " %op_result_pre %c_one %c_zero\n";
}
else
{
fullOperation += "%op_result_u32 = OpSelect %" + selectType + " %op_result_pre %c_one %c_zero\n";
if (m_typeSize == 32)
fullOperation += "%op_result = OpBitcast %" + m_spirvTestType + " %op_result_u32\n";
else
fullOperation += "%op_result = OpSConvert %" + m_spirvTestType + " %op_result_u32\n";
}
}
else
{
DE_ASSERT(resultName == "%op_result");
}
}
template <class T>
bool SpvAsmTypeTests<T>::filterNone (T)
{
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterNone (T, T)
{
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterNone (T, T, T)
{
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterNone (T, T, T, T)
{
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterZero (T, T b)
{
if (b == static_cast<T>(0))
return false;
else
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterNegativesAndZero (T a, T b)
{
if (a < static_cast<T>(0) || b <= static_cast<T>(0))
return false;
else
return true;
}
template <class T>
bool SpvAsmTypeTests<T>::filterMinGtMax (T, T a, T b)
{
if (a > b)
return false;
else
return true;
}
template <class T>
T SpvAsmTypeTests<T>::zero (T)
{
return static_cast<T>(0.0);
}
template <class T>
T SpvAsmTypeTests<T>::zero (T, T)
{
return static_cast<T>(0.0);
}
template <class T>
T SpvAsmTypeTests<T>::zero (T, T, T)
{
return static_cast<T>(0.0);
}
template <class T>
T SpvAsmTypeTests<T>::zero (T, T, T, T)
{
return static_cast<T>(0.0);
}
template <class T>
std::string SpvAsmTypeTests<T>::replicate (const std::string& replicant,
const deUint32 count)
{
std::string result;
for (deUint32 i = 0; i < count; ++i)
result += replicant;
return result;
}
class SpvAsmTypeInt8Tests : public SpvAsmTypeTests<deInt8>
{
public:
SpvAsmTypeInt8Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeInt8Tests (void);
void getDataset (vector<deInt8>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deInt8>& data);
};
SpvAsmTypeInt8Tests::SpvAsmTypeInt8Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "i8", "int8 tests", DE_NULL, "Int8", "OpTypeInt 8 1", TYPE_I8, 8, vectorSize)
{
m_cases[0] = -42;
m_cases[1] = 73;
m_cases[2] = 121;
}
SpvAsmTypeInt8Tests::~SpvAsmTypeInt8Tests (void)
{
}
void SpvAsmTypeInt8Tests::getDataset (vector<deInt8>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0);
input.push_back(static_cast<deInt8>(deIntMinValue32(8)));// A 8-bit negative number
input.push_back(static_cast<deInt8>(deIntMaxValue32(8)));// A 8-bit positive number
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(static_cast<deInt8>(m_rnd.getUint8()));
}
void SpvAsmTypeInt8Tests::pushResource (vector<Resource>& resource,
vector<deInt8>& data)
{
resource.push_back(Resource(BufferSp(new Int8Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeInt16Tests : public SpvAsmTypeTests<deInt16>
{
public:
SpvAsmTypeInt16Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeInt16Tests (void);
void getDataset (vector<deInt16>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deInt16>& data);
};
SpvAsmTypeInt16Tests::SpvAsmTypeInt16Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "i16", "int16 tests", "shaderInt16", "Int16", "OpTypeInt 16 1", TYPE_I16, 16, vectorSize)
{
m_cases[0] = -3221;
m_cases[1] = 3210;
m_cases[2] = 19597;
}
SpvAsmTypeInt16Tests::~SpvAsmTypeInt16Tests (void)
{
}
void SpvAsmTypeInt16Tests::getDataset (vector<deInt16>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0);
input.push_back(static_cast<deInt16>(deIntMinValue32(16)));// A 16-bit negative number
input.push_back(static_cast<deInt16>(deIntMaxValue32(16)));// A 16-bit positive number
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(static_cast<deInt16>(m_rnd.getUint16()));
}
void SpvAsmTypeInt16Tests::pushResource (vector<Resource>& resource,
vector<deInt16>& data)
{
resource.push_back(Resource(BufferSp(new Int16Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeInt32Tests : public SpvAsmTypeTests<deInt32>
{
public:
SpvAsmTypeInt32Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeInt32Tests (void);
void getDataset (vector<deInt32>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deInt32>& data);
};
SpvAsmTypeInt32Tests::SpvAsmTypeInt32Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "i32", "int32 tests", DE_NULL, DE_NULL, "OpTypeInt 32 1", TYPE_I32, 32, vectorSize)
{
m_cases[0] = -3221;
m_cases[1] = 3210;
m_cases[2] = 268438669;
}
SpvAsmTypeInt32Tests::~SpvAsmTypeInt32Tests (void)
{
}
void SpvAsmTypeInt32Tests::getDataset (vector<deInt32>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0);
input.push_back(deIntMinValue32(32) + 1); // So MIN = -MAX
input.push_back(deIntMaxValue32(32));
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(static_cast<deInt32>(m_rnd.getUint32()));
}
void SpvAsmTypeInt32Tests::pushResource (vector<Resource>& resource,
vector<deInt32>& data)
{
resource.push_back(Resource(BufferSp(new Int32Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeInt64Tests : public SpvAsmTypeTests<deInt64>
{
public:
SpvAsmTypeInt64Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeInt64Tests (void);
void getDataset (vector<deInt64>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deInt64>& data);
};
SpvAsmTypeInt64Tests::SpvAsmTypeInt64Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "i64", "int64 tests", "shaderInt64", "Int64", "OpTypeInt 64 1", TYPE_I64, 64, vectorSize)
{
m_cases[0] = 3210;
m_cases[1] = -268438669;
m_cases[2] = 26843866939192872;
}
SpvAsmTypeInt64Tests::~SpvAsmTypeInt64Tests (void)
{
}
void SpvAsmTypeInt64Tests::getDataset (vector<deInt64>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0);
input.push_back(0xFFFF859A3BF78592);// A 64-bit negative number
input.push_back(0x7FFF859A3BF78592);// A 64-bit positive number
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(static_cast<deInt64>(m_rnd.getUint64()));
}
void SpvAsmTypeInt64Tests::pushResource (vector<Resource>& resource,
vector<deInt64>& data)
{
resource.push_back(Resource(BufferSp(new Int64Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeUint8Tests : public SpvAsmTypeTests<deUint8>
{
public:
SpvAsmTypeUint8Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeUint8Tests (void);
void getDataset (vector<deUint8>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deUint8>& data);
};
SpvAsmTypeUint8Tests::SpvAsmTypeUint8Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "u8", "uint8 tests", DE_NULL, "Int8", "OpTypeInt 8 0", TYPE_U8, 8, vectorSize)
{
m_cases[0] = 0;
m_cases[1] = 73;
m_cases[2] = 193;
}
SpvAsmTypeUint8Tests::~SpvAsmTypeUint8Tests (void)
{
}
void SpvAsmTypeUint8Tests::getDataset (vector<deUint8>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0); // Min value
input.push_back(~0); // Max value
//Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(m_rnd.getUint8());
}
void SpvAsmTypeUint8Tests::pushResource (vector<Resource>& resource,
vector<deUint8>& data)
{
resource.push_back(Resource(BufferSp(new Uint8Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeUint16Tests : public SpvAsmTypeTests<deUint16>
{
public:
SpvAsmTypeUint16Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeUint16Tests (void);
void getDataset (vector<deUint16>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deUint16>& data);
};
SpvAsmTypeUint16Tests::SpvAsmTypeUint16Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "u16", "uint16 tests", "shaderInt16", "Int16", "OpTypeInt 16 0", TYPE_U16, 16, vectorSize)
{
m_cases[0] = 0;
m_cases[1] = 3210;
m_cases[2] = 19597;
}
SpvAsmTypeUint16Tests::~SpvAsmTypeUint16Tests (void)
{
}
void SpvAsmTypeUint16Tests::getDataset (vector<deUint16>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0); // Min value
input.push_back(~0); // Max value
//Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(m_rnd.getUint16());
}
void SpvAsmTypeUint16Tests::pushResource (vector<Resource>& resource,
vector<deUint16>& data)
{
resource.push_back(Resource(BufferSp(new Uint16Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeUint32Tests : public SpvAsmTypeTests<deUint32>
{
public:
SpvAsmTypeUint32Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeUint32Tests (void);
void getDataset (vector<deUint32>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deUint32>& data);
};
SpvAsmTypeUint32Tests::SpvAsmTypeUint32Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "u32", "uint32 tests", DE_NULL, DE_NULL, "OpTypeInt 32 0", TYPE_U32, 32, vectorSize)
{
m_cases[0] = 0;
m_cases[1] = 3210;
m_cases[2] = 268438669;
}
SpvAsmTypeUint32Tests::~SpvAsmTypeUint32Tests (void)
{
}
void SpvAsmTypeUint32Tests::getDataset (vector<deUint32>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0); // Min value
input.push_back(~0); // Max value
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(m_rnd.getUint32());
}
void SpvAsmTypeUint32Tests::pushResource (vector<Resource>& resource,
vector<deUint32>& data)
{
resource.push_back(Resource(BufferSp(new Uint32Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
class SpvAsmTypeUint64Tests : public SpvAsmTypeTests<deUint64>
{
public:
SpvAsmTypeUint64Tests (tcu::TestContext& testCtx,
deUint32 vectorSize);
~SpvAsmTypeUint64Tests (void);
void getDataset (vector<deUint64>& input,
deUint32 numElements);
void pushResource (vector<Resource>& resource,
vector<deUint64>& data);
};
SpvAsmTypeUint64Tests::SpvAsmTypeUint64Tests (tcu::TestContext& testCtx,
deUint32 vectorSize)
: SpvAsmTypeTests (testCtx, "u64", "uint64 tests", "shaderInt64", "Int64", "OpTypeInt 64 0", TYPE_U64, 64, vectorSize)
{
m_cases[0] = 3210;
m_cases[1] = 268438669;
m_cases[2] = 26843866939192872;
}
SpvAsmTypeUint64Tests::~SpvAsmTypeUint64Tests (void)
{
}
void SpvAsmTypeUint64Tests::getDataset (vector<deUint64>& input,
deUint32 numElements)
{
// Push first special cases
input.push_back(0); // Min value
input.push_back(~0); // Max value
// Push switch cases
input.push_back(m_cases[0]);
input.push_back(m_cases[1]);
input.push_back(m_cases[2]);
numElements -= static_cast<deUint32>(input.size());
// Random values
for (deUint32 elemNdx = 0; elemNdx < numElements; ++elemNdx)
input.push_back(m_rnd.getUint64());
}
void SpvAsmTypeUint64Tests::pushResource (vector<Resource>& resource,
vector<deUint64>& data)
{
resource.push_back(Resource(BufferSp(new Uint64Buffer(data)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
}
template <class T>
class TestMath
{
public:
static inline T test_abs (T x)
{
T t0 = static_cast<T>(0.0);
if (x >= t0)
return x;
else
return test_negate(x);
}
static inline T test_add (T x, T y)
{
return static_cast<T>(x + y);
}
static inline T test_clamp (T x, T minVal, T maxVal)
{
return test_min(test_max(x, minVal), maxVal);
}
static inline T test_div (T x, T y)
{
// In SPIR-V, if "y" is 0, then the result is undefined. In our case,
// let's return 0
if (y == static_cast<T>(0))
return 0;
else
return static_cast<T>(x / y);
}
static inline T test_lsb (T x)
{
for (deUint32 i = 0; i < 8 * sizeof(T); i++)
{
if (x & (1u << i))
return static_cast<T>(i);
}
return static_cast<T>(-1.0);
}
static inline T test_max (T x, T y)
{
if (x < y)
return y;
else
return x;
}
static inline T test_min (T x, T y)
{
if (y < x)
return y;
else
return x;
}
static inline T test_mod (T x, T y)
{
T sign_x, sign_y;
// In SPIR-V, if "y" is 0, then the result is undefined. In our case,
// let's return 0
if (y == static_cast<T>(0))
return 0;
if (x >= static_cast<T>(0))
sign_x = 1;
else
sign_x = -1;
if (y >= static_cast<T>(0))
sign_y = 1;
else
sign_y = -1;
return static_cast<T>(static_cast<T>(static_cast<T>(x) - static_cast<T>(y * static_cast<T>(x / y))) * static_cast<T>(sign_y * sign_x));
}
static inline T test_mul (T x, T y)
{
return static_cast<T>(x * y);
}
static inline T test_negate (T x)
{
return static_cast<T>(static_cast<T>(0.0) - static_cast<T>(x));
}
static inline T test_rem (T x, T y)
{
// In SPIR-V, if "y" is 0, then the result is undefined. In our case,
// let's return 0
if (y == static_cast<T>(0))
return 0;
return static_cast<T>(x % y);
}
static inline T test_sign (T x)
{
T t0 = static_cast<T>(0.0);
if (x > t0)
return static_cast<T>(1.0);
else if (x < t0)
return static_cast<T>(-1.0);
else
return t0;
}
static inline T test_sub (T x, T y)
{
return static_cast<T>(x - y);
}
static inline T test_msb (T)
{
TCU_THROW(InternalError, "Not implemented");
}
static inline T test_lsr (T x, T y)
{
if (x >= static_cast<T>(0) || y == static_cast<T>(0))
{
return static_cast<T>(x >> y);
}
else
{
const T mask = de::leftZeroMask(y);
return static_cast<T>((x >> y) & mask);
}
}
static inline T test_asr (T x, T y)
{
const T bitmask = static_cast<T>(deUint64(1) << (sizeof(T) * 8u - 1u));
if ((x & bitmask) && y > 0)
{
const T mask = de::leftSetMask(y);
const T result = static_cast<T>((x >> y) | mask);
return result;
}
else
{
return static_cast<T>(x >> y);
}
}
static inline T test_lsl (T x, T y)
{
return static_cast<T>(x << y);
}
static inline T test_bitwise_or (T x, T y)
{
return static_cast<T>(x | y);
}
static inline T test_bitwise_xor (T x, T y)
{
return static_cast<T>(x ^ y);
}
static inline T test_bitwise_and (T x, T y)
{
return static_cast<T>(x