blob: 2c20612e84b849875f88cedc8059cc3594c82aa2 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2018 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 VK_KHR_shader_float_controls tests.
*//*--------------------------------------------------------------------*/
#include "vktSpvAsmFloatControlsTests.hpp"
#include "vktSpvAsmComputeShaderCase.hpp"
#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
#include "vktTestGroupUtil.hpp"
#include "tcuFloat.hpp"
#include "tcuFloatFormat.hpp"
#include "tcuStringTemplate.hpp"
#include "deUniquePtr.hpp"
#include "deFloat16.h"
#include "vkQueryUtil.hpp"
#include "vkRefUtil.hpp"
#include <cstring>
#include <vector>
#include <limits>
#include <fenv.h>
namespace vkt
{
namespace SpirVAssembly
{
namespace
{
using namespace std;
using namespace tcu;
enum FloatType
{
FP16 = 0,
FP32,
FP64
};
enum class BufferDataType
{
DATA_UNKNOWN = 0,
DATA_FP16 = 1,
DATA_FP32 = 2,
DATA_FP64 = 3,
};
enum FloatUsage
{
// If the float type is 16bit, then the use of the type is supported by
// VK_KHR_16bit_storage.
FLOAT_STORAGE_ONLY = 0,
// Use of the float type goes beyond VK_KHR_16bit_storage.
FLOAT_ARITHMETIC
};
enum FloatStatementUsageBits
{
B_STATEMENT_USAGE_ARGS_CONST_FLOAT = (1<<0 ),
B_STATEMENT_USAGE_ARGS_CONST_FP16 = (1<<1 ),
B_STATEMENT_USAGE_ARGS_CONST_FP32 = (1<<2 ),
B_STATEMENT_USAGE_ARGS_CONST_FP64 = (1<<3 ),
B_STATEMENT_USAGE_TYPES_TYPE_FLOAT = (1<<4 ),
B_STATEMENT_USAGE_TYPES_TYPE_FP16 = (1<<5 ),
B_STATEMENT_USAGE_TYPES_TYPE_FP32 = (1<<6 ),
B_STATEMENT_USAGE_TYPES_TYPE_FP64 = (1<<7 ),
B_STATEMENT_USAGE_CONSTS_TYPE_FLOAT = (1<<8 ),
B_STATEMENT_USAGE_CONSTS_TYPE_FP16 = (1<<9 ),
B_STATEMENT_USAGE_CONSTS_TYPE_FP32 = (1<<10),
B_STATEMENT_USAGE_CONSTS_TYPE_FP64 = (1<<11),
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT = (1<<12),
B_STATEMENT_USAGE_COMMANDS_CONST_FP16 = (1<<13),
B_STATEMENT_USAGE_COMMANDS_CONST_FP32 = (1<<14),
B_STATEMENT_USAGE_COMMANDS_CONST_FP64 = (1<<15),
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT = (1<<16),
B_STATEMENT_USAGE_COMMANDS_TYPE_FP16 = (1<<17),
B_STATEMENT_USAGE_COMMANDS_TYPE_FP32 = (1<<18),
B_STATEMENT_USAGE_COMMANDS_TYPE_FP64 = (1<<19),
};
typedef deUint32 FloatStatementUsageFlags;
// Enum containing float behaviors that its possible to test.
enum BehaviorFlagBits
{
B_DENORM_PRESERVE = 0x00000001, // DenormPreserve
B_DENORM_FLUSH = 0x00000002, // DenormFlushToZero
B_ZIN_PRESERVE = 0x00000004, // SignedZeroInfNanPreserve
B_RTE_ROUNDING = 0x00000008, // RoundingModeRTE
B_RTZ_ROUNDING = 0x00000010 // RoundingModeRTZ
};
typedef deUint32 BehaviorFlags;
// Codes for all float values used in tests as arguments and operation results
// This approach allows to replace values with different types reducing complexity of the tests implementation
enum ValueId
{
// common values used as both arguments and results
V_UNUSED = 0, // used to mark arguments that are not used in operation
V_MINUS_INF, // or results of tests cases that should be skipped
V_MINUS_ONE, // -1.0
V_MINUS_ZERO, // -0.0
V_ZERO, // 0.0
V_HALF, // 0.5
V_ONE, // 1.0
V_INF,
V_DENORM,
V_NAN,
// arguments for rounding mode tests - used only when arguments are passed from input
V_ADD_ARG_A,
V_ADD_ARG_B,
V_SUB_ARG_A,
V_SUB_ARG_B,
V_MUL_ARG_A,
V_MUL_ARG_B,
V_DOT_ARG_A,
V_DOT_ARG_B,
// arguments of conversion operations - used only when arguments are passed from input
V_CONV_FROM_FP32_ARG,
V_CONV_FROM_FP64_ARG,
// arguments of rounding operations
V_ADD_RTZ_RESULT,
V_ADD_RTE_RESULT,
V_SUB_RTZ_RESULT,
V_SUB_RTE_RESULT,
V_MUL_RTZ_RESULT,
V_MUL_RTE_RESULT,
V_DOT_RTZ_RESULT,
V_DOT_RTE_RESULT,
// non comon results of some operation - corner cases
V_ZERO_OR_DENORM_TIMES_TWO, // fp16 addition of non-flushed denorm with itself (or equivalent dot-product or vector-matrix multiply)
V_MINUS_ONE_OR_CLOSE, // value used only for fp16 subtraction result of preserved denorm and one
V_PI_DIV_2,
V_ZERO_OR_MINUS_ZERO, // both +0 and -0 are accepted
V_ZERO_OR_ONE, // both +0 and 1 are accepted
V_ZERO_OR_FP16_DENORM_TO_FP32, // both 0 and fp32 representation of fp16 denorm are accepted
V_ZERO_OR_FP16_DENORM_TO_FP64,
V_ZERO_OR_FP32_DENORM_TO_FP64,
V_DENORM_TIMES_TWO,
V_DEGREES_DENORM,
V_TRIG_ONE, // 1.0 trigonometric operations, including precision margin
V_MINUS_INF_OR_LOG_DENORM,
V_MINUS_INF_OR_LOG2_DENORM,
V_ZERO_OR_SQRT_DENORM,
V_INF_OR_INV_SQRT_DENORM,
//results of conversion operations
V_CONV_TO_FP16_RTZ_RESULT,
V_CONV_TO_FP16_RTE_RESULT,
V_CONV_TO_FP32_RTZ_RESULT,
V_CONV_TO_FP32_RTE_RESULT,
V_CONV_DENORM_SMALLER, // used e.g. when converting fp16 denorm to fp32
V_CONV_DENORM_BIGGER,
};
// Enum containing all tested operatios. Operations are defined in generic way so that
// they can be used to generate tests operating on arguments with different values of
// specified float type.
enum OperationId
{
// spir-v unary operations
OID_NEGATE = 0,
OID_COMPOSITE,
OID_COMPOSITE_INS,
OID_COPY,
OID_D_EXTRACT,
OID_D_INSERT,
OID_SHUFFLE,
OID_TRANSPOSE,
OID_CONV_FROM_FP16,
OID_CONV_FROM_FP32,
OID_CONV_FROM_FP64,
OID_SCONST_CONV_FROM_FP32_TO_FP16,
OID_SCONST_CONV_FROM_FP64_TO_FP32,
OID_SCONST_CONV_FROM_FP64_TO_FP16,
OID_RETURN_VAL,
// spir-v binary operations
OID_ADD,
OID_SUB,
OID_MUL,
OID_DIV,
OID_REM,
OID_MOD,
OID_PHI,
OID_SELECT,
OID_DOT,
OID_VEC_MUL_S,
OID_VEC_MUL_M,
OID_MAT_MUL_S,
OID_MAT_MUL_V,
OID_MAT_MUL_M,
OID_OUT_PROD,
OID_ORD_EQ,
OID_UORD_EQ,
OID_ORD_NEQ,
OID_UORD_NEQ,
OID_ORD_LS,
OID_UORD_LS,
OID_ORD_GT,
OID_UORD_GT,
OID_ORD_LE,
OID_UORD_LE,
OID_ORD_GE,
OID_UORD_GE,
// glsl unary operations
OID_ROUND,
OID_ROUND_EV,
OID_TRUNC,
OID_ABS,
OID_SIGN,
OID_FLOOR,
OID_CEIL,
OID_FRACT,
OID_RADIANS,
OID_DEGREES,
OID_SIN,
OID_COS,
OID_TAN,
OID_ASIN,
OID_ACOS,
OID_ATAN,
OID_SINH,
OID_COSH,
OID_TANH,
OID_ASINH,
OID_ACOSH,
OID_ATANH,
OID_EXP,
OID_LOG,
OID_EXP2,
OID_LOG2,
OID_SQRT,
OID_INV_SQRT,
OID_MODF,
OID_MODF_ST,
OID_FREXP,
OID_FREXP_ST,
OID_LENGHT,
OID_NORMALIZE,
OID_REFLECT,
OID_REFRACT,
OID_MAT_DET,
OID_MAT_INV,
OID_PH_DENORM, // PackHalf2x16
OID_UPH_DENORM,
OID_PD_DENORM, // PackDouble2x32
OID_UPD_DENORM_FLUSH,
OID_UPD_DENORM_PRESERVE,
// glsl binary operations
OID_ATAN2,
OID_POW,
OID_MIX,
OID_FMA,
OID_MIN,
OID_MAX,
OID_CLAMP,
OID_STEP,
OID_SSTEP,
OID_DIST,
OID_CROSS,
OID_FACE_FWD,
OID_NMIN,
OID_NMAX,
OID_NCLAMP,
OID_ORTE_ROUND,
OID_ORTZ_ROUND
};
// Structures storing data required to test DenormPreserve and DenormFlushToZero modes.
// Operations are separated into binary and unary lists because binary operations can be tested with
// two attributes and thus denorms can be tested in combination with value, denorm, inf and nan.
// Unary operations are only tested with denorms.
struct BinaryCase
{
OperationId operationId;
ValueId opVarResult;
ValueId opDenormResult;
ValueId opInfResult;
ValueId opNanResult;
};
struct UnaryCase
{
OperationId operationId;
ValueId result;
};
// Function replacing all occurrences of substring with string passed in last parameter.
string replace(string str, const string& from, const string& to)
{
// to keep spir-v code clean and easier to read parts of it are processed
// with this method instead of StringTemplate; main usage of this method is the
// replacement of "float_" with "f16_", "f32_" or "f64_" depending on test case
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
// Structure used to perform bits conversion int type <-> float type.
template<typename FLOAT_TYPE, typename UINT_TYPE>
struct RawConvert
{
union Value
{
FLOAT_TYPE fp;
UINT_TYPE ui;
};
};
// Traits used to get int type that can store equivalent float type.
template<typename FLOAT_TYPE>
struct GetCoresponding
{
typedef deUint16 uint_type;
};
template<>
struct GetCoresponding<float>
{
typedef deUint32 uint_type;
};
template<>
struct GetCoresponding<double>
{
typedef deUint64 uint_type;
};
// All values used for arguments and operation results are stored in single map.
// Each float type (fp16, fp32, fp64) has its own map that is used during
// test setup and during verification. TypeValuesBase is interface to that map.
class TypeValuesBase
{
public:
TypeValuesBase();
virtual ~TypeValuesBase() = default;
virtual BufferSp constructInputBuffer (const ValueId* twoArguments) const = 0;
virtual BufferSp constructOutputBuffer (ValueId result) const = 0;
virtual void fillInputData (const ValueId* twoArguments, vector<deUint8>& bufferData, deUint32& offset) const = 0;
protected:
const double pi;
};
TypeValuesBase::TypeValuesBase()
: pi(3.14159265358979323846)
{
}
typedef de::SharedPtr<TypeValuesBase> TypeValuesSP;
template <typename FLOAT_TYPE>
class TypeValues: public TypeValuesBase
{
public:
TypeValues();
BufferSp constructInputBuffer (const ValueId* twoArguments) const override;
BufferSp constructOutputBuffer (ValueId result) const override;
void fillInputData (const ValueId* twoArguments, vector<deUint8>& bufferData, deUint32& offset) const override;
FLOAT_TYPE getValue(ValueId id) const;
template <typename UINT_TYPE>
FLOAT_TYPE exactByteEquivalent(UINT_TYPE byteValue) const;
private:
typedef map<ValueId, FLOAT_TYPE> ValueMap;
ValueMap m_valueIdToFloatType;
};
template <typename FLOAT_TYPE>
BufferSp TypeValues<FLOAT_TYPE>::constructInputBuffer(const ValueId* twoArguments) const
{
std::vector<FLOAT_TYPE> inputData(2);
inputData[0] = m_valueIdToFloatType.at(twoArguments[0]);
inputData[1] = m_valueIdToFloatType.at(twoArguments[1]);
return BufferSp(new Buffer<FLOAT_TYPE>(inputData));
}
template <typename FLOAT_TYPE>
BufferSp TypeValues<FLOAT_TYPE>::constructOutputBuffer(ValueId result) const
{
// note: we are not doing maping here, ValueId is directly saved in
// float type in order to be able to retireve it during verification
typedef typename GetCoresponding<FLOAT_TYPE>::uint_type uint_t;
uint_t value = static_cast<uint_t>(result);
// For FP16 we increase the buffer size to hold an unsigned integer, as
// we can be in the no 16bit_storage case.
const uint_t outputSize = sizeof(FLOAT_TYPE) == 2u ? 2u : 1u;
std::vector<FLOAT_TYPE> outputData(outputSize, exactByteEquivalent<uint_t>(value));
return BufferSp(new Buffer<FLOAT_TYPE>(outputData));
}
template <typename FLOAT_TYPE>
void TypeValues<FLOAT_TYPE>::fillInputData(const ValueId* twoArguments, vector<deUint8>& bufferData, deUint32& offset) const
{
deUint32 typeSize = sizeof(FLOAT_TYPE);
FLOAT_TYPE argA = getValue(twoArguments[0]);
deMemcpy(&bufferData[offset], &argA, typeSize);
offset += typeSize;
FLOAT_TYPE argB = getValue(twoArguments[1]);
deMemcpy(&bufferData[offset], &argB, typeSize);
offset += typeSize;
}
template <typename FLOAT_TYPE>
FLOAT_TYPE TypeValues<FLOAT_TYPE>::getValue(ValueId id) const
{
return m_valueIdToFloatType.at(id);
}
template <typename FLOAT_TYPE>
template <typename UINT_TYPE>
FLOAT_TYPE TypeValues<FLOAT_TYPE>::exactByteEquivalent(UINT_TYPE byteValue) const
{
typename RawConvert<FLOAT_TYPE, UINT_TYPE>::Value value;
value.ui = byteValue;
return value.fp;
}
template <>
TypeValues<deFloat16>::TypeValues()
: TypeValuesBase()
{
// NOTE: when updating entries in m_valueIdToFloatType make sure to
// update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
ValueMap& vm = m_valueIdToFloatType;
vm[V_UNUSED] = deFloat32To16(0.0f);
vm[V_MINUS_INF] = 0xfc00;
vm[V_MINUS_ONE] = deFloat32To16(-1.0f);
vm[V_MINUS_ZERO] = 0x8000;
vm[V_ZERO] = 0x0000;
vm[V_HALF] = deFloat32To16(0.5f);
vm[V_ONE] = deFloat32To16(1.0f);
vm[V_INF] = 0x7c00;
vm[V_DENORM] = 0x03f0; // this value should be the same as the result of denormBase - epsilon
vm[V_NAN] = 0x7cf0;
vm[V_PI_DIV_2] = 0x3e48;
vm[V_DENORM_TIMES_TWO] = 0x07e0;
vm[V_DEGREES_DENORM] = 0x1b0c;
vm[V_ADD_ARG_A] = 0x3c03;
vm[V_ADD_ARG_B] = vm[V_ONE];
vm[V_SUB_ARG_A] = vm[V_ADD_ARG_A];
vm[V_SUB_ARG_B] = 0x4203;
vm[V_MUL_ARG_A] = vm[V_ADD_ARG_A];
vm[V_MUL_ARG_B] = 0x1900;
vm[V_DOT_ARG_A] = vm[V_ADD_ARG_A];
vm[V_DOT_ARG_B] = vm[V_MUL_ARG_B];
vm[V_CONV_FROM_FP32_ARG] = vm[V_UNUSED];
vm[V_CONV_FROM_FP64_ARG] = vm[V_UNUSED];
vm[V_ADD_RTZ_RESULT] = 0x4001; // deFloat16Add(vm[V_ADD_ARG_A], vm[V_ADD_ARG_B], rtz)
vm[V_SUB_RTZ_RESULT] = 0xc001; // deFloat16Sub(vm[V_SUB_ARG_A], vm[V_SUB_ARG_B], rtz)
vm[V_MUL_RTZ_RESULT] = 0x1903; // deFloat16Mul(vm[V_MUL_ARG_A], vm[V_MUL_ARG_B], rtz)
vm[V_DOT_RTZ_RESULT] = 0x1d03;
vm[V_CONV_TO_FP16_RTZ_RESULT] = deFloat32To16Round(1.22334445f, DE_ROUNDINGMODE_TO_ZERO);
vm[V_CONV_TO_FP32_RTZ_RESULT] = vm[V_UNUSED];
vm[V_ADD_RTE_RESULT] = 0x4002; // deFloat16Add(vm[V_ADD_ARG_A], vm[V_ADD_ARG_B], rte)
vm[V_SUB_RTE_RESULT] = 0xc002; // deFloat16Sub(vm[V_SUB_ARG_A], vm[V_SUB_ARG_B], rte)
vm[V_MUL_RTE_RESULT] = 0x1904; // deFloat16Mul(vm[V_MUL_ARG_A], vm[V_MUL_ARG_B], rte)
vm[V_DOT_RTE_RESULT] = 0x1d04;
vm[V_CONV_TO_FP16_RTE_RESULT] = deFloat32To16Round(1.22334445f, DE_ROUNDINGMODE_TO_NEAREST_EVEN);
vm[V_CONV_TO_FP32_RTE_RESULT] = vm[V_UNUSED];
// there is no precision to store fp32 denorm nor fp64 denorm
vm[V_CONV_DENORM_SMALLER] = vm[V_ZERO];
vm[V_CONV_DENORM_BIGGER] = vm[V_ZERO];
}
template <>
TypeValues<float>::TypeValues()
: TypeValuesBase()
{
// NOTE: when updating entries in m_valueIdToFloatType make sure to
// update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
ValueMap& vm = m_valueIdToFloatType;
vm[V_UNUSED] = 0.0f;
vm[V_MINUS_INF] = -std::numeric_limits<float>::infinity();
vm[V_MINUS_ONE] = -1.0f;
vm[V_MINUS_ZERO] = -0.0f;
vm[V_ZERO] = 0.0f;
vm[V_HALF] = 0.5f;
vm[V_ONE] = 1.0f;
vm[V_INF] = std::numeric_limits<float>::infinity();
vm[V_DENORM] = static_cast<float>(1.413e-42); // 0x000003f0
vm[V_NAN] = std::numeric_limits<float>::quiet_NaN();
vm[V_PI_DIV_2] = static_cast<float>(pi / 2);
vm[V_DENORM_TIMES_TWO] = vm[V_DENORM] + vm[V_DENORM];
vm[V_DEGREES_DENORM] = deFloatDegrees(vm[V_DENORM]);
float e = std::numeric_limits<float>::epsilon();
vm[V_ADD_ARG_A] = 1.0f + 3 * e;
vm[V_ADD_ARG_B] = 1.0f;
vm[V_SUB_ARG_A] = vm[V_ADD_ARG_A];
vm[V_SUB_ARG_B] = 3.0f + 6 * e;
vm[V_MUL_ARG_A] = vm[V_ADD_ARG_A];
vm[V_MUL_ARG_B] = 5 * e;
vm[V_DOT_ARG_A] = vm[V_ADD_ARG_A];
vm[V_DOT_ARG_B] = 5 * e;
vm[V_CONV_FROM_FP32_ARG] = 1.22334445f;
vm[V_CONV_FROM_FP64_ARG] = vm[V_UNUSED];
int prevRound = fegetround();
fesetround(FE_TOWARDZERO);
vm[V_ADD_RTZ_RESULT] = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
vm[V_SUB_RTZ_RESULT] = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
vm[V_MUL_RTZ_RESULT] = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
vm[V_DOT_RTZ_RESULT] = vm[V_MUL_RTZ_RESULT] + vm[V_MUL_RTZ_RESULT];
vm[V_CONV_TO_FP16_RTZ_RESULT] = vm[V_UNUSED];
vm[V_CONV_TO_FP32_RTZ_RESULT] = exactByteEquivalent<deUint32>(0x3f9c968d); // result of conversion from double(1.22334455)
fesetround(FE_TONEAREST);
vm[V_ADD_RTE_RESULT] = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
vm[V_SUB_RTE_RESULT] = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
vm[V_MUL_RTE_RESULT] = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
vm[V_DOT_RTE_RESULT] = vm[V_MUL_RTE_RESULT] + vm[V_MUL_RTE_RESULT];
vm[V_CONV_TO_FP16_RTE_RESULT] = vm[V_UNUSED];
vm[V_CONV_TO_FP32_RTE_RESULT] = exactByteEquivalent<deUint32>(0x3f9c968e); // result of conversion from double(1.22334455)
fesetround(prevRound);
// there is no precision to store fp64 denorm
vm[V_CONV_DENORM_SMALLER] = exactByteEquivalent<deUint32>(0x387c0000); // fp16 denorm
vm[V_CONV_DENORM_BIGGER] = vm[V_ZERO];
}
template <>
TypeValues<double>::TypeValues()
: TypeValuesBase()
{
// NOTE: when updating entries in m_valueIdToFloatType make sure to
// update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
ValueMap& vm = m_valueIdToFloatType;
vm[V_UNUSED] = 0.0;
vm[V_MINUS_INF] = -std::numeric_limits<double>::infinity();
vm[V_MINUS_ONE] = -1.0;
vm[V_MINUS_ZERO] = -0.0;
vm[V_ZERO] = 0.0;
vm[V_HALF] = 0.5;
vm[V_ONE] = 1.0;
vm[V_INF] = std::numeric_limits<double>::infinity();
vm[V_DENORM] = 4.98e-321; // 0x00000000000003F0
vm[V_NAN] = std::numeric_limits<double>::quiet_NaN();
vm[V_PI_DIV_2] = pi / 2;
vm[V_DENORM_TIMES_TWO] = vm[V_DENORM] + vm[V_DENORM];
vm[V_DEGREES_DENORM] = vm[V_UNUSED];
double e = std::numeric_limits<double>::epsilon();
vm[V_ADD_ARG_A] = 1.0 + 3 * e;
vm[V_ADD_ARG_B] = 1.0;
vm[V_SUB_ARG_A] = vm[V_ADD_ARG_A];
vm[V_SUB_ARG_B] = 3.0 + 6 * e;
vm[V_MUL_ARG_A] = vm[V_ADD_ARG_A];
vm[V_MUL_ARG_B] = 5 * e;
vm[V_DOT_ARG_A] = vm[V_ADD_ARG_A];
vm[V_DOT_ARG_B] = 5 * e;
vm[V_CONV_FROM_FP32_ARG] = vm[V_UNUSED];
vm[V_CONV_FROM_FP64_ARG] = 1.22334455;
int prevRound = fegetround();
fesetround(FE_TOWARDZERO);
vm[V_ADD_RTZ_RESULT] = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
vm[V_SUB_RTZ_RESULT] = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
vm[V_MUL_RTZ_RESULT] = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
vm[V_DOT_RTZ_RESULT] = vm[V_MUL_RTZ_RESULT] + vm[V_MUL_RTZ_RESULT];
vm[V_CONV_TO_FP16_RTZ_RESULT] = vm[V_UNUSED];
vm[V_CONV_TO_FP32_RTZ_RESULT] = vm[V_UNUSED];
fesetround(FE_TONEAREST);
vm[V_ADD_RTE_RESULT] = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
vm[V_SUB_RTE_RESULT] = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
vm[V_MUL_RTE_RESULT] = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
vm[V_DOT_RTE_RESULT] = vm[V_MUL_RTE_RESULT] + vm[V_MUL_RTE_RESULT];
vm[V_CONV_TO_FP16_RTE_RESULT] = vm[V_UNUSED];
vm[V_CONV_TO_FP32_RTE_RESULT] = vm[V_UNUSED];
fesetround(prevRound);
vm[V_CONV_DENORM_SMALLER] = exactByteEquivalent<deUint64>(0x3f0f800000000000); // 0x03f0 is fp16 denorm
vm[V_CONV_DENORM_BIGGER] = exactByteEquivalent<deUint64>(0x373f800000000000); // 0x000003f0 is fp32 denorm
}
// Each float type (fp16, fp32, fp64) has specific set of SPIR-V snippets
// that was extracted to separate template specialization. Those snippets
// are used to compose final test shaders. With this approach
// parameterization can be done just once per type and reused for many tests.
class TypeSnippetsBase
{
public:
virtual ~TypeSnippetsBase() = default;
protected:
void updateSpirvSnippets();
public: // Type specific data:
// Number of bits consumed by float type
string bitWidth;
// Minimum positive normal
string epsilon;
// denormBase is a normal value (found empirically) used to generate denorm value.
// Denorm is generated by substracting epsilon from denormBase.
// denormBase is not a denorm - it is used to create denorm.
// This value is needed when operations are tested with arguments that were
// generated in the code. Generated denorm should be the same as denorm
// used when arguments are passed via input (m_valueIdToFloatType[V_DENORM]).
// This is required as result of some operations depends on actual denorm value
// e.g. OpRadians(0x0001) is 0 but OpRadians(0x03f0) is denorm.
string denormBase;
string capabilities;
string extensions;
string capabilitiesFp16Without16BitStorage;
string extensionsFp16Without16BitStorage;
string arrayStride;
bool loadStoreRequiresShaderFloat16;
public: // Type specific spir-v snippets:
// Common annotations
string typeAnnotationsSnippet;
// Definitions of all types commonly used by operation tests
string typeDefinitionsSnippet;
// Definitions of all types commonly used by settings tests
string minTypeDefinitionsSnippet;
// Definitions of all constants commonly used by tests
string constantsDefinitionsSnippet;
// Map that stores instructions that generate arguments of specified value.
// Every test that uses generated inputod will select up to two items from this map
typedef map<ValueId, string> SnippetMap;
SnippetMap valueIdToSnippetArgMap;
// Spir-v snippets that read argument from SSBO
string argumentsFromInputSnippet;
string multiArgumentsFromInputSnippet;
// SSBO with stage input/output definitions
string inputAnnotationsSnippet;
string inputDefinitionsSnippet;
string outputAnnotationsSnippet;
string multiOutputAnnotationsSnippet;
string outputDefinitionsSnippet;
string multiOutputDefinitionsSnippet;
// Varying is required to pass result from vertex stage to fragment stage,
// one of requirements was to not use SSBO writes in vertex stage so we
// need to do that in fragment stage; we also cant pass operation result
// directly because of interpolation, to avoid it we do a bitcast to uint
string varyingsTypesSnippet;
string inputVaryingsSnippet;
string outputVaryingsSnippet;
string storeVertexResultSnippet;
string loadVertexResultSnippet;
string storeResultsSnippet;
string multiStoreResultsSnippet;
string argumentsFromInputFp16Snippet;
string storeResultsFp16Snippet;
string multiArgumentsFromInputFp16Snippet;
string multiOutputAnnotationsFp16Snippet;
string multiStoreResultsFp16Snippet;
string multiOutputDefinitionsFp16Snippet;
string inputDefinitionsFp16Snippet;
string outputDefinitionsFp16Snippet;
string typeAnnotationsFp16Snippet;
string typeDefinitionsFp16Snippet;
};
void TypeSnippetsBase::updateSpirvSnippets()
{
// annotations to types that are commonly used by tests
const string typeAnnotationsTemplate =
"OpDecorate %type_float_arr_1 ArrayStride " + arrayStride + "\n"
"OpDecorate %type_float_arr_2 ArrayStride " + arrayStride + "\n";
// definition off all types that are commonly used by tests
const string typeDefinitionsTemplate =
"%type_float = OpTypeFloat " + bitWidth + "\n"
"%type_float_uptr = OpTypePointer Uniform %type_float\n"
"%type_float_fptr = OpTypePointer Function %type_float\n"
"%type_float_vec2 = OpTypeVector %type_float 2\n"
"%type_float_vec3 = OpTypeVector %type_float 3\n"
"%type_float_vec4 = OpTypeVector %type_float 4\n"
"%type_float_vec4_iptr = OpTypePointer Input %type_float_vec4\n"
"%type_float_vec4_optr = OpTypePointer Output %type_float_vec4\n"
"%type_float_mat2x2 = OpTypeMatrix %type_float_vec2 2\n"
"%type_float_arr_1 = OpTypeArray %type_float %c_i32_1\n"
"%type_float_arr_2 = OpTypeArray %type_float %c_i32_2\n";
// minimal type definition set that is used by settings tests
const string minTypeDefinitionsTemplate =
"%type_float = OpTypeFloat " + bitWidth + "\n"
"%type_float_uptr = OpTypePointer Uniform %type_float\n"
"%type_float_arr_2 = OpTypeArray %type_float %c_i32_2\n";
// definition off all constants that are used by tests
const string constantsDefinitionsTemplate =
"%c_float_n1 = OpConstant %type_float -1\n"
"%c_float_0 = OpConstant %type_float 0.0\n"
"%c_float_0_5 = OpConstant %type_float 0.5\n"
"%c_float_1 = OpConstant %type_float 1\n"
"%c_float_2 = OpConstant %type_float 2\n"
"%c_float_3 = OpConstant %type_float 3\n"
"%c_float_4 = OpConstant %type_float 4\n"
"%c_float_5 = OpConstant %type_float 5\n"
"%c_float_6 = OpConstant %type_float 6\n"
"%c_float_eps = OpConstant %type_float " + epsilon + "\n"
"%c_float_denorm_base = OpConstant %type_float " + denormBase + "\n";
// when arguments are read from SSBO this snipped is placed in main function
const string argumentsFromInputTemplate =
"%arg1loc = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_0\n"
"%arg1 = OpLoad %type_float %arg1loc\n"
"%arg2loc = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_1\n"
"%arg2 = OpLoad %type_float %arg2loc\n";
const string multiArgumentsFromInputTemplate =
"%arg1_float_loc = OpAccessChain %type_float_uptr %ssbo_in %c_i32_${attr} %c_i32_0\n"
"%arg2_float_loc = OpAccessChain %type_float_uptr %ssbo_in %c_i32_${attr} %c_i32_1\n"
"%arg1_float = OpLoad %type_float %arg1_float_loc\n"
"%arg2_float = OpLoad %type_float %arg2_float_loc\n";
// when tested shader stage reads from SSBO it has to have this snippet
inputAnnotationsSnippet =
"OpMemberDecorate %SSBO_in 0 Offset 0\n"
"OpDecorate %SSBO_in BufferBlock\n"
"OpDecorate %ssbo_in DescriptorSet 0\n"
"OpDecorate %ssbo_in Binding 0\n"
"OpDecorate %ssbo_in NonWritable\n";
const string inputDefinitionsTemplate =
"%SSBO_in = OpTypeStruct %type_float_arr_2\n"
"%up_SSBO_in = OpTypePointer Uniform %SSBO_in\n"
"%ssbo_in = OpVariable %up_SSBO_in Uniform\n";
outputAnnotationsSnippet =
"OpMemberDecorate %SSBO_out 0 Offset 0\n"
"OpDecorate %SSBO_out BufferBlock\n"
"OpDecorate %ssbo_out DescriptorSet 0\n"
"OpDecorate %ssbo_out Binding 1\n";
const string multiOutputAnnotationsTemplate =
"OpMemberDecorate %SSBO_float_out 0 Offset 0\n"
"OpDecorate %type_float_arr_2 ArrayStride "+ arrayStride + "\n"
"OpDecorate %SSBO_float_out BufferBlock\n"
"OpDecorate %ssbo_float_out DescriptorSet 0\n";
const string outputDefinitionsTemplate =
"%SSBO_out = OpTypeStruct %type_float_arr_1\n"
"%up_SSBO_out = OpTypePointer Uniform %SSBO_out\n"
"%ssbo_out = OpVariable %up_SSBO_out Uniform\n";
const string multiOutputDefinitionsTemplate =
"%SSBO_float_out = OpTypeStruct %type_float\n"
"%up_SSBO_float_out = OpTypePointer Uniform %SSBO_float_out\n"
"%ssbo_float_out = OpVariable %up_SSBO_float_out Uniform\n";
// this snippet is used by compute and fragment stage but not by vertex stage
const string storeResultsTemplate =
"%outloc = OpAccessChain %type_float_uptr %ssbo_out %c_i32_0 %c_i32_0\n"
"OpStore %outloc %result\n";
const string multiStoreResultsTemplate =
"%outloc" + bitWidth + " = OpAccessChain %type_float_uptr %ssbo_float_out %c_i32_0\n"
" OpStore %outloc" + bitWidth + " %result" + bitWidth + "\n";
const string typeToken = "_float";
const string typeName = "_f" + bitWidth;
typeAnnotationsSnippet = replace(typeAnnotationsTemplate, typeToken, typeName);
typeDefinitionsSnippet = replace(typeDefinitionsTemplate, typeToken, typeName);
minTypeDefinitionsSnippet = replace(minTypeDefinitionsTemplate, typeToken, typeName);
constantsDefinitionsSnippet = replace(constantsDefinitionsTemplate, typeToken, typeName);
argumentsFromInputSnippet = replace(argumentsFromInputTemplate, typeToken, typeName);
multiArgumentsFromInputSnippet = replace(multiArgumentsFromInputTemplate, typeToken, typeName);
inputDefinitionsSnippet = replace(inputDefinitionsTemplate, typeToken, typeName);
multiOutputAnnotationsSnippet = replace(multiOutputAnnotationsTemplate, typeToken, typeName);
outputDefinitionsSnippet = replace(outputDefinitionsTemplate, typeToken, typeName);
multiOutputDefinitionsSnippet = replace(multiOutputDefinitionsTemplate, typeToken, typeName);
storeResultsSnippet = replace(storeResultsTemplate, typeToken, typeName);
multiStoreResultsSnippet = replace(multiStoreResultsTemplate, typeToken, typeName);
argumentsFromInputFp16Snippet = "";
storeResultsFp16Snippet = "";
multiArgumentsFromInputFp16Snippet = "";
multiOutputAnnotationsFp16Snippet = "";
multiStoreResultsFp16Snippet = "";
multiOutputDefinitionsFp16Snippet = "";
inputDefinitionsFp16Snippet = "";
typeAnnotationsFp16Snippet = "";
outputDefinitionsFp16Snippet = "";
typeDefinitionsFp16Snippet = "";
if (bitWidth.compare("16") == 0)
{
typeDefinitionsFp16Snippet =
"%type_u32_uptr = OpTypePointer Uniform %type_u32\n"
"%type_u32_arr_1 = OpTypeArray %type_u32 %c_i32_1\n";
typeAnnotationsFp16Snippet = "OpDecorate %type_u32_arr_1 ArrayStride 4\n";
const string inputToken = "_f16_arr_2";
const string inputName = "_u32_arr_1";
inputDefinitionsFp16Snippet = replace(inputDefinitionsSnippet, inputToken, inputName);
argumentsFromInputFp16Snippet =
"%argloc = OpAccessChain %type_u32_uptr %ssbo_in %c_i32_0 %c_i32_0\n"
"%inval = OpLoad %type_u32 %argloc\n"
"%arg = OpBitcast %type_f16_vec2 %inval\n"
"%arg1 = OpCompositeExtract %type_f16 %arg 0\n"
"%arg2 = OpCompositeExtract %type_f16 %arg 1\n";
const string outputToken = "_f16_arr_1";
const string outputName = "_u32_arr_1";
outputDefinitionsFp16Snippet = replace(outputDefinitionsSnippet, outputToken, outputName);
storeResultsFp16Snippet =
"%result_f16_vec2 = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0\n"
"%result_u32 = OpBitcast %type_u32 %result_f16_vec2\n"
"%outloc = OpAccessChain %type_u32_uptr %ssbo_out %c_i32_0 %c_i32_0\n"
"OpStore %outloc %result_u32\n";
multiArgumentsFromInputFp16Snippet =
"%arg_u32_loc = OpAccessChain %type_u32_uptr %ssbo_in %c_i32_${attr} %c_i32_0\n"
"%arg_u32 = OpLoad %type_u32 %arg_u32_loc\n"
"%arg_f16_vec2 = OpBitcast %type_f16_vec2 %arg_u32\n"
"%arg1_f16 = OpCompositeExtract %type_f16 %arg_f16_vec2 0\n"
"%arg2_f16 = OpCompositeExtract %type_f16 %arg_f16_vec2 1\n";
multiOutputAnnotationsFp16Snippet =
"OpMemberDecorate %SSBO_u32_out 0 Offset 0\n"
"OpDecorate %type_u32_arr_1 ArrayStride 4\n"
"OpDecorate %SSBO_u32_out BufferBlock\n"
"OpDecorate %ssbo_u32_out DescriptorSet 0\n";
multiStoreResultsFp16Snippet =
"%outloc_u32 = OpAccessChain %type_u32_uptr %ssbo_u32_out %c_i32_0\n"
"%result16_vec2 = OpCompositeConstruct %type_f16_vec2 %result16 %c_f16_0\n"
"%result_u32 = OpBitcast %type_u32 %result16_vec2\n"
" OpStore %outloc_u32 %result_u32\n";
multiOutputDefinitionsFp16Snippet =
"%c_f16_0 = OpConstant %type_f16 0.0\n"
"%SSBO_u32_out = OpTypeStruct %type_u32\n"
"%up_SSBO_u32_out = OpTypePointer Uniform %SSBO_u32_out\n"
"%ssbo_u32_out = OpVariable %up_SSBO_u32_out Uniform\n";
}
// NOTE: only values used as _generated_ arguments in test operations
// need to be in this map, arguments that are only used by tests,
// that grab arguments from input, do need to be in this map
// NOTE: when updating entries in valueIdToSnippetArgMap make
// sure to update also m_valueIdToFloatType for all float width
SnippetMap& sm = valueIdToSnippetArgMap;
sm[V_UNUSED] = "OpFSub %type_float %c_float_0 %c_float_0\n";
sm[V_MINUS_INF] = "OpFDiv %type_float %c_float_n1 %c_float_0\n";
sm[V_MINUS_ONE] = "OpFAdd %type_float %c_float_n1 %c_float_0\n";
sm[V_MINUS_ZERO] = "OpFMul %type_float %c_float_n1 %c_float_0\n";
sm[V_ZERO] = "OpFMul %type_float %c_float_0 %c_float_0\n";
sm[V_HALF] = "OpFAdd %type_float %c_float_0_5 %c_float_0\n";
sm[V_ONE] = "OpFAdd %type_float %c_float_1 %c_float_0\n";
sm[V_INF] = "OpFDiv %type_float %c_float_1 %c_float_0\n"; // x / 0 == Inf
sm[V_DENORM] = "OpFSub %type_float %c_float_denorm_base %c_float_eps\n";
sm[V_NAN] = "OpFDiv %type_float %c_float_0 %c_float_0\n"; // 0 / 0 == Nan
map<ValueId, string>::iterator it;
for ( it = sm.begin(); it != sm.end(); it++ )
sm[it->first] = replace(it->second, typeToken, typeName);
}
typedef de::SharedPtr<TypeSnippetsBase> TypeSnippetsSP;
template<typename FLOAT_TYPE>
class TypeSnippets: public TypeSnippetsBase
{
public:
TypeSnippets();
};
template<>
TypeSnippets<deFloat16>::TypeSnippets()
{
bitWidth = "16";
epsilon = "6.104e-5"; // 2^-14 = 0x0400
// 1.2113e-4 is 0x07f0 which after substracting epsilon will give 0x03f0 (same as vm[V_DENORM])
// NOTE: constants in SPIR-V cant be specified as exact fp16 - there is conversion from double to fp16
denormBase = "1.2113e-4";
capabilities = "OpCapability StorageUniform16\n";
extensions = "OpExtension \"SPV_KHR_16bit_storage\"\n";
capabilitiesFp16Without16BitStorage = "OpCapability Float16\n";
extensionsFp16Without16BitStorage = "";
arrayStride = "2";
varyingsTypesSnippet =
"%type_u32_iptr = OpTypePointer Input %type_u32\n"
"%type_u32_optr = OpTypePointer Output %type_u32\n";
inputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_iptr Input\n";
outputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_optr Output\n";
storeVertexResultSnippet =
"%tmp_vec2 = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0\n"
"%packed_result = OpBitcast %type_u32 %tmp_vec2\n"
"OpStore %BP_vertex_result %packed_result\n";
loadVertexResultSnippet =
"%packed_result = OpLoad %type_u32 %BP_vertex_result\n"
"%tmp_vec2 = OpBitcast %type_f16_vec2 %packed_result\n"
"%result = OpCompositeExtract %type_f16 %tmp_vec2 0\n";
loadStoreRequiresShaderFloat16 = true;
updateSpirvSnippets();
}
template<>
TypeSnippets<float>::TypeSnippets()
{
bitWidth = "32";
epsilon = "1.175494351e-38";
denormBase = "1.1756356e-38";
capabilities = "";
extensions = "";
capabilitiesFp16Without16BitStorage = "";
extensionsFp16Without16BitStorage = "";
arrayStride = "4";
varyingsTypesSnippet =
"%type_u32_iptr = OpTypePointer Input %type_u32\n"
"%type_u32_optr = OpTypePointer Output %type_u32\n";
inputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_iptr Input\n";
outputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_optr Output\n";
storeVertexResultSnippet =
"%packed_result = OpBitcast %type_u32 %result\n"
"OpStore %BP_vertex_result %packed_result\n";
loadVertexResultSnippet =
"%packed_result = OpLoad %type_u32 %BP_vertex_result\n"
"%result = OpBitcast %type_f32 %packed_result\n";
loadStoreRequiresShaderFloat16 = false;
updateSpirvSnippets();
}
template<>
TypeSnippets<double>::TypeSnippets()
{
bitWidth = "64";
epsilon = "2.2250738585072014e-308"; // 0x0010000000000000
denormBase = "2.2250738585076994e-308"; // 0x00100000000003F0
capabilities = "OpCapability Float64\n";
extensions = "";
capabilitiesFp16Without16BitStorage = "";
extensionsFp16Without16BitStorage = "";
arrayStride = "8";
varyingsTypesSnippet =
"%type_u32_vec2_iptr = OpTypePointer Input %type_u32_vec2\n"
"%type_u32_vec2_optr = OpTypePointer Output %type_u32_vec2\n";
inputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_vec2_iptr Input\n";
outputVaryingsSnippet =
"%BP_vertex_result = OpVariable %type_u32_vec2_optr Output\n";
storeVertexResultSnippet =
"%packed_result = OpBitcast %type_u32_vec2 %result\n"
"OpStore %BP_vertex_result %packed_result\n";
loadVertexResultSnippet =
"%packed_result = OpLoad %type_u32_vec2 %BP_vertex_result\n"
"%result = OpBitcast %type_f64 %packed_result\n";
loadStoreRequiresShaderFloat16 = false;
updateSpirvSnippets();
}
class TypeTestResultsBase
{
public:
virtual ~TypeTestResultsBase() {}
FloatType floatType() const;
protected:
FloatType m_floatType;
public:
// Vectors containing test data for float controls
vector<BinaryCase> binaryOpFTZ;
vector<UnaryCase> unaryOpFTZ;
vector<BinaryCase> binaryOpDenormPreserve;
vector<UnaryCase> unaryOpDenormPreserve;
};
FloatType TypeTestResultsBase::floatType() const
{
return m_floatType;
}
typedef de::SharedPtr<TypeTestResultsBase> TypeTestResultsSP;
template<typename FLOAT_TYPE>
class TypeTestResults: public TypeTestResultsBase
{
public:
TypeTestResults();
};
template<>
TypeTestResults<deFloat16>::TypeTestResults()
{
m_floatType = FP16;
// note: there are many FTZ test cases that can produce diferent result depending
// on input denorm being flushed or not; because of that FTZ tests can be limited
// to those that return denorm as those are the ones affected by tested extension
const BinaryCase binaryOpFTZArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_ADD, V_ONE, V_ZERO_OR_DENORM_TIMES_TWO,
V_INF, V_UNUSED },
{ OID_SUB, V_MINUS_ONE, V_ZERO, V_MINUS_INF, V_UNUSED },
{ OID_MUL, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DIV, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_REM, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_MOD, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_M, V_ZERO_OR_DENORM_TIMES_TWO,
V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_V, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_M, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_OUT_PROD, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DOT, V_ZERO_OR_DENORM_TIMES_TWO,
V_ZERO, V_UNUSED, V_UNUSED },
{ OID_ATAN2, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_POW, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_MIX, V_HALF, V_ZERO, V_INF, V_UNUSED },
{ OID_MIN, V_ZERO, V_ZERO, V_ZERO, V_UNUSED },
{ OID_MAX, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_STEP, V_ONE, V_ONE, V_ONE, V_UNUSED },
{ OID_SSTEP, V_HALF, V_ONE, V_ZERO, V_UNUSED },
{ OID_FMA, V_HALF, V_HALF, V_UNUSED, V_UNUSED },
{ OID_FACE_FWD, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE },
{ OID_NMIN, V_ZERO, V_ZERO, V_ZERO, V_ZERO },
{ OID_NMAX, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_NCLAMP, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_DIST, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CROSS, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
};
const UnaryCase unaryOpFTZArr[] = {
//operation op den
{ OID_NEGATE, V_MINUS_ZERO },
{ OID_ROUND, V_ZERO },
{ OID_ROUND_EV, V_ZERO },
{ OID_TRUNC, V_ZERO },
{ OID_ABS, V_ZERO },
{ OID_FLOOR, V_ZERO },
{ OID_CEIL, V_ZERO_OR_ONE },
{ OID_FRACT, V_ZERO },
{ OID_RADIANS, V_ZERO },
{ OID_DEGREES, V_ZERO },
{ OID_SIN, V_ZERO },
{ OID_COS, V_TRIG_ONE },
{ OID_TAN, V_ZERO },
{ OID_ASIN, V_ZERO },
{ OID_ACOS, V_PI_DIV_2 },
{ OID_ATAN, V_ZERO },
{ OID_SINH, V_ZERO },
{ OID_COSH, V_ONE },
{ OID_TANH, V_ZERO },
{ OID_ASINH, V_ZERO },
{ OID_ACOSH, V_UNUSED },
{ OID_ATANH, V_ZERO },
{ OID_EXP, V_ONE },
{ OID_LOG, V_MINUS_INF_OR_LOG_DENORM },
{ OID_EXP2, V_ONE },
{ OID_LOG2, V_MINUS_INF_OR_LOG2_DENORM },
{ OID_SQRT, V_ZERO_OR_SQRT_DENORM },
{ OID_INV_SQRT, V_INF_OR_INV_SQRT_DENORM },
{ OID_MAT_DET, V_ZERO },
{ OID_MAT_INV, V_ZERO_OR_MINUS_ZERO },
{ OID_MODF, V_ZERO },
{ OID_MODF_ST, V_ZERO },
{ OID_NORMALIZE, V_ZERO },
{ OID_REFLECT, V_ZERO },
{ OID_REFRACT, V_ZERO },
{ OID_LENGHT, V_ZERO },
};
const BinaryCase binaryOpDenormPreserveArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_PHI, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_SELECT, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_ADD, V_ONE, V_DENORM_TIMES_TWO, V_INF, V_NAN },
{ OID_SUB, V_MINUS_ONE_OR_CLOSE, V_ZERO, V_MINUS_INF, V_NAN },
{ OID_MUL, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_M, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_V, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_M, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_OUT_PROD, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_DOT, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MIX, V_HALF, V_DENORM, V_INF, V_NAN },
{ OID_FMA, V_HALF, V_HALF, V_INF, V_NAN },
{ OID_MIN, V_DENORM, V_DENORM, V_DENORM, V_UNUSED },
{ OID_MAX, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_NMIN, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_NMAX, V_ONE, V_DENORM, V_INF, V_DENORM },
{ OID_NCLAMP, V_ONE, V_DENORM, V_INF, V_DENORM },
};
const UnaryCase unaryOpDenormPreserveArr[] = {
//operation op den
{ OID_RETURN_VAL, V_DENORM },
{ OID_D_EXTRACT, V_DENORM },
{ OID_D_INSERT, V_DENORM },
{ OID_SHUFFLE, V_DENORM },
{ OID_COMPOSITE, V_DENORM },
{ OID_COMPOSITE_INS, V_DENORM },
{ OID_COPY, V_DENORM },
{ OID_TRANSPOSE, V_DENORM },
{ OID_NEGATE, V_DENORM },
{ OID_ABS, V_DENORM },
{ OID_SIGN, V_ONE },
{ OID_RADIANS, V_DENORM },
{ OID_DEGREES, V_DEGREES_DENORM },
};
binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
}
template<>
TypeTestResults<float>::TypeTestResults()
{
m_floatType = FP32;
const BinaryCase binaryOpFTZArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_ADD, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_SUB, V_MINUS_ONE, V_ZERO, V_MINUS_INF, V_UNUSED },
{ OID_MUL, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DIV, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_REM, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_MOD, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_M, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_V, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_M, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_OUT_PROD, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DOT, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_ATAN2, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_POW, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_MIX, V_HALF, V_ZERO, V_INF, V_UNUSED },
{ OID_MIN, V_ZERO, V_ZERO, V_ZERO, V_UNUSED },
{ OID_MAX, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_STEP, V_ONE, V_ONE, V_ONE, V_UNUSED },
{ OID_SSTEP, V_HALF, V_ONE, V_ZERO, V_UNUSED },
{ OID_FMA, V_HALF, V_HALF, V_UNUSED, V_UNUSED },
{ OID_FACE_FWD, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE },
{ OID_NMIN, V_ZERO, V_ZERO, V_ZERO, V_ZERO },
{ OID_NMAX, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_NCLAMP, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_DIST, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CROSS, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
};
const UnaryCase unaryOpFTZArr[] = {
//operation op den
{ OID_NEGATE, V_MINUS_ZERO },
{ OID_ROUND, V_ZERO },
{ OID_ROUND_EV, V_ZERO },
{ OID_TRUNC, V_ZERO },
{ OID_ABS, V_ZERO },
{ OID_FLOOR, V_ZERO },
{ OID_CEIL, V_ZERO_OR_ONE },
{ OID_FRACT, V_ZERO },
{ OID_RADIANS, V_ZERO },
{ OID_DEGREES, V_ZERO },
{ OID_SIN, V_ZERO },
{ OID_COS, V_TRIG_ONE },
{ OID_TAN, V_ZERO },
{ OID_ASIN, V_ZERO },
{ OID_ACOS, V_PI_DIV_2 },
{ OID_ATAN, V_ZERO },
{ OID_SINH, V_ZERO },
{ OID_COSH, V_ONE },
{ OID_TANH, V_ZERO },
{ OID_ASINH, V_ZERO },
{ OID_ACOSH, V_UNUSED },
{ OID_ATANH, V_ZERO },
{ OID_EXP, V_ONE },
{ OID_LOG, V_MINUS_INF_OR_LOG_DENORM },
{ OID_EXP2, V_ONE },
{ OID_LOG2, V_MINUS_INF_OR_LOG2_DENORM },
{ OID_SQRT, V_ZERO_OR_SQRT_DENORM },
{ OID_INV_SQRT, V_INF_OR_INV_SQRT_DENORM },
{ OID_MAT_DET, V_ZERO },
{ OID_MAT_INV, V_ZERO_OR_MINUS_ZERO },
{ OID_MODF, V_ZERO },
{ OID_MODF_ST, V_ZERO },
{ OID_NORMALIZE, V_ZERO },
{ OID_REFLECT, V_ZERO },
{ OID_REFRACT, V_ZERO },
{ OID_LENGHT, V_ZERO },
};
const BinaryCase binaryOpDenormPreserveArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_PHI, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_SELECT, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_ADD, V_ONE, V_DENORM_TIMES_TWO, V_INF, V_NAN },
{ OID_SUB, V_MINUS_ONE, V_ZERO, V_MINUS_INF, V_NAN },
{ OID_MUL, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_M, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_V, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_M, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_OUT_PROD, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_DOT, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MIX, V_HALF, V_DENORM, V_INF, V_NAN },
{ OID_FMA, V_HALF, V_HALF, V_INF, V_NAN },
{ OID_MIN, V_DENORM, V_DENORM, V_DENORM, V_UNUSED },
{ OID_MAX, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_NMIN, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_NMAX, V_ONE, V_DENORM, V_INF, V_DENORM },
{ OID_NCLAMP, V_ONE, V_DENORM, V_INF, V_DENORM },
};
const UnaryCase unaryOpDenormPreserveArr[] = {
//operation op den
{ OID_RETURN_VAL, V_DENORM },
{ OID_D_EXTRACT, V_DENORM },
{ OID_D_INSERT, V_DENORM },
{ OID_SHUFFLE, V_DENORM },
{ OID_COMPOSITE, V_DENORM },
{ OID_COMPOSITE_INS, V_DENORM },
{ OID_COPY, V_DENORM },
{ OID_TRANSPOSE, V_DENORM },
{ OID_NEGATE, V_DENORM },
{ OID_ABS, V_DENORM },
{ OID_SIGN, V_ONE },
{ OID_RADIANS, V_DENORM },
{ OID_DEGREES, V_DEGREES_DENORM },
};
binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
}
template<>
TypeTestResults<double>::TypeTestResults()
{
m_floatType = FP64;
// fp64 is supported by fewer operations then fp16 and fp32
// e.g. Radians and Degrees functions are not supported
const BinaryCase binaryOpFTZArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_ADD, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_SUB, V_MINUS_ONE, V_ZERO, V_MINUS_INF, V_UNUSED },
{ OID_MUL, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DIV, V_ZERO, V_UNUSED, V_ZERO, V_UNUSED },
{ OID_REM, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_MOD, V_ZERO, V_UNUSED, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_VEC_MUL_M, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_S, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_V, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MAT_MUL_M, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_OUT_PROD, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_DOT, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
{ OID_MIX, V_HALF, V_ZERO, V_INF, V_UNUSED },
{ OID_MIN, V_ZERO, V_ZERO, V_ZERO, V_UNUSED },
{ OID_MAX, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_STEP, V_ONE, V_ONE, V_ONE, V_UNUSED },
{ OID_SSTEP, V_HALF, V_ONE, V_ZERO, V_UNUSED },
{ OID_FMA, V_HALF, V_HALF, V_UNUSED, V_UNUSED },
{ OID_FACE_FWD, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE, V_MINUS_ONE },
{ OID_NMIN, V_ZERO, V_ZERO, V_ZERO, V_ZERO },
{ OID_NMAX, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_NCLAMP, V_ONE, V_ZERO, V_INF, V_ZERO },
{ OID_DIST, V_ONE, V_ZERO, V_INF, V_UNUSED },
{ OID_CROSS, V_ZERO, V_ZERO, V_UNUSED, V_UNUSED },
};
const UnaryCase unaryOpFTZArr[] = {
//operation op den
{ OID_NEGATE, V_MINUS_ZERO },
{ OID_ROUND, V_ZERO },
{ OID_ROUND_EV, V_ZERO },
{ OID_TRUNC, V_ZERO },
{ OID_ABS, V_ZERO },
{ OID_FLOOR, V_ZERO },
{ OID_CEIL, V_ZERO_OR_ONE },
{ OID_FRACT, V_ZERO },
{ OID_SQRT, V_ZERO_OR_SQRT_DENORM },
{ OID_INV_SQRT, V_INF_OR_INV_SQRT_DENORM },
{ OID_MAT_DET, V_ZERO },
{ OID_MAT_INV, V_ZERO_OR_MINUS_ZERO },
{ OID_MODF, V_ZERO },
{ OID_MODF_ST, V_ZERO },
{ OID_NORMALIZE, V_ZERO },
{ OID_REFLECT, V_ZERO },
{ OID_LENGHT, V_ZERO },
};
const BinaryCase binaryOpDenormPreserveArr[] = {
//operation den op one den op den den op inf den op nan
{ OID_PHI, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_SELECT, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_ADD, V_ONE, V_DENORM_TIMES_TWO, V_INF, V_NAN },
{ OID_SUB, V_MINUS_ONE, V_ZERO, V_MINUS_INF, V_NAN },
{ OID_MUL, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_VEC_MUL_M, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_S, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_V, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MAT_MUL_M, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_OUT_PROD, V_DENORM, V_ZERO, V_INF, V_NAN },
{ OID_DOT, V_DENORM_TIMES_TWO, V_ZERO, V_INF, V_NAN },
{ OID_MIX, V_HALF, V_DENORM, V_INF, V_NAN },
{ OID_FMA, V_HALF, V_HALF, V_INF, V_NAN },
{ OID_MIN, V_DENORM, V_DENORM, V_DENORM, V_UNUSED },
{ OID_MAX, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_CLAMP, V_ONE, V_DENORM, V_INF, V_UNUSED },
{ OID_NMIN, V_DENORM, V_DENORM, V_DENORM, V_DENORM },
{ OID_NMAX, V_ONE, V_DENORM, V_INF, V_DENORM },
{ OID_NCLAMP, V_ONE, V_DENORM, V_INF, V_DENORM },
};
const UnaryCase unaryOpDenormPreserveArr[] = {
//operation op den
{ OID_RETURN_VAL, V_DENORM },
{ OID_D_EXTRACT, V_DENORM },
{ OID_D_INSERT, V_DENORM },
{ OID_SHUFFLE, V_DENORM },
{ OID_COMPOSITE, V_DENORM },
{ OID_COMPOSITE_INS, V_DENORM },
{ OID_COPY, V_DENORM },
{ OID_TRANSPOSE, V_DENORM },
{ OID_NEGATE, V_DENORM },
{ OID_ABS, V_DENORM },
{ OID_SIGN, V_ONE },
};
binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
}
// Operation structure holds data needed to test specified SPIR-V operation. This class contains
// additional annotations, additional types and aditional constants that should be properly included
// in SPIR-V code. Commands attribute in this structure contains code that performs tested operation
// on given arguments, in some cases verification is also performed there.
// All snipets stroed in this structure are generic and can be specialized for fp16, fp32 or fp64,
// thanks to that this data can be shared by many OperationTestCase instances (testing diferent
// float behaviours on diferent float widths).
struct Operation
{
// operation name is included in test case name
const char* name;
// How extensively is the floating point type used?
FloatUsage floatUsage;
// operation specific spir-v snippets that will be
// placed in proper places in final test shader
const char* annotations;
const char* types;
const char* constants;
const char* variables;
const char* functions;
const char* commands;
// conversion operations operate on one float type and produce float
// type with different bit width; restrictedInputType is used only when
// isInputTypeRestricted is set to true and it restricts usage of this
// operation to specified input type
bool isInputTypeRestricted;
FloatType restrictedInputType;
// arguments for OpSpecConstant need to be specified also as constant
bool isSpecConstant;
// set if c_float* constant is used in operation
FloatStatementUsageFlags statementUsageFlags;
Operation() {}
// Minimal constructor - used by most of operations
Operation(const char* _name, FloatUsage _floatUsage, const char* _commands, const FloatStatementUsageFlags _statementUsageFlags = 0)
: name(_name)
, floatUsage(_floatUsage)
, annotations("")
, types("")
, constants("")
, variables("")
, functions("")
, commands(_commands)
, isInputTypeRestricted(false)
, restrictedInputType(FP16) // not used as isInputTypeRestricted is false
, isSpecConstant(false)
, statementUsageFlags(_statementUsageFlags)
{}
// Conversion operations constructor (used also by conversions done in SpecConstantOp)
Operation(const char* _name,
FloatUsage _floatUsage,
bool specConstant,
FloatType _inputType,
const char* _constants,
const char* _commands,
const FloatStatementUsageFlags _statementUsageFlags = 0)
: name(_name)
, floatUsage(_floatUsage)
, annotations("")
, types("")
, constants(_constants)
, variables("")
, functions("")
, commands(_commands)
, isInputTypeRestricted(true)
, restrictedInputType(_inputType)
, isSpecConstant(specConstant)
, statementUsageFlags(_statementUsageFlags)
{}
// Full constructor - used by few operations, that are more complex to test
Operation(const char* _name,
FloatUsage _floatUsage,
const char* _annotations,
const char* _types,
const char* _constants,
const char* _variables,
const char* _functions,
const char* _commands,
const FloatStatementUsageFlags _statementUsageFlags = 0)
: name(_name)
, floatUsage(_floatUsage)
, annotations(_annotations)
, types(_types)
, constants(_constants)
, variables(_variables)
, functions(_functions)
, commands(_commands)
, isInputTypeRestricted(false)
, restrictedInputType(FP16) // not used as isInputTypeRestricted is false
, isSpecConstant(false)
, statementUsageFlags(_statementUsageFlags)
{}
// Full constructor - used by rounding override cases
Operation(const char* _name,
FloatUsage _floatUsage,
FloatType _inputType,
const char* _annotations,
const char* _types,
const char* _constants,
const char* _commands,
const FloatStatementUsageFlags _statementUsageFlags = 0)
: name(_name)
, floatUsage(_floatUsage)
, annotations(_annotations)
, types(_types)
, constants(_constants)
, variables("")
, functions("")
, commands(_commands)
, isInputTypeRestricted(true)
, restrictedInputType(_inputType)
, isSpecConstant(false)
, statementUsageFlags(_statementUsageFlags)
{}
};
// Class storing input that will be passed to operation and expected
// output that should be generated for specified behaviour.
class OperationTestCase
{
public:
OperationTestCase() {}
OperationTestCase(const char* _baseName,
BehaviorFlags _behaviorFlags,
OperationId _operatinId,
ValueId _input1,
ValueId _input2,
ValueId _expectedOutput,
deBool _fp16Without16BitStorage = DE_FALSE)
: baseName(_baseName)
, behaviorFlags(_behaviorFlags)
, operationId(_operatinId)
, expectedOutput(_expectedOutput)
, fp16Without16BitStorage(_fp16Without16BitStorage)
{
input[0] = _input1;
input[1] = _input2;
}
public:
string baseName;
BehaviorFlags behaviorFlags;
OperationId operationId;
ValueId input[2];
ValueId expectedOutput;
deBool fp16Without16BitStorage;
};
// Helper structure used to store specialized operation
// data. This data is ready to be used during shader assembly.
struct SpecializedOperation
{
string constants;
string annotations;
string types;
string arguments;
string variables;
string functions;
string commands;
FloatType inFloatType;
TypeSnippetsSP inTypeSnippets;
TypeSnippetsSP outTypeSnippets;
FloatStatementUsageFlags argumentsUsesFloatConstant;
};
// Class responsible for constructing list of test cases for specified
// float type and specified way of preparation of arguments.
// Arguments can be either read from input SSBO or generated via math
// operations in spir-v code.
class TestCasesBuilder
{
public:
void init();
void build(vector<OperationTestCase>& testCases, TypeTestResultsSP typeTestResults, bool argumentsFromInput);
const Operation& getOperation(OperationId id) const;
private:
void createUnaryTestCases(vector<OperationTestCase>& testCases,
OperationId operationId,
ValueId denormPreserveResult,
ValueId denormFTZResult,
deBool fp16WithoutStorage = DE_FALSE) const;
private:
// Operations are shared betwean test cases so they are
// passed to them as pointers to data stored in TestCasesBuilder.
typedef OperationTestCase OTC;
typedef Operation Op;
map<int, Op> m_operations;
};
void TestCasesBuilder::init()
{
map<int, Op>& mo = m_operations;
// predefine operations repeatedly used in tests; note that "_float"
// in every operation command will be replaced with either "_f16",
// "_f32" or "_f64" - StringTemplate is not used here because it
// would make code less readable
// m_operations contains generic operation definitions that can be
// used for all float types
mo[OID_NEGATE] = Op("negate", FLOAT_ARITHMETIC,
"%result = OpFNegate %type_float %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_COMPOSITE] = Op("composite", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%result = OpCompositeExtract %type_float %vec1 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_COMPOSITE_INS] = Op("comp_ins", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_0\n"
"%vec2 = OpCompositeInsert %type_float_vec2 %arg1 %vec1 0\n"
"%result = OpCompositeExtract %type_float %vec2 0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_COPY] = Op("copy", FLOAT_STORAGE_ONLY,
"%result = OpCopyObject %type_float %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_D_EXTRACT] = Op("extract", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%result = OpVectorExtractDynamic %type_float %vec1 %c_i32_0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_D_INSERT] = Op("insert", FLOAT_ARITHMETIC,
"%tmpVec = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n"
"%vec1 = OpVectorInsertDynamic %type_float_vec2 %tmpVec %arg1 %c_i32_0\n"
"%result = OpCompositeExtract %type_float %vec1 0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SHUFFLE] = Op("shuffle", FLOAT_ARITHMETIC,
"%tmpVec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%tmpVec2 = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n" // NOTE: its impossible to test shuffle with denorms flushed
"%vec1 = OpVectorShuffle %type_float_vec2 %tmpVec1 %tmpVec2 0 2\n" // to zero as this will be done by earlier operation
"%result = OpCompositeExtract %type_float %vec1 0\n", // (this also applies to few other operations)
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_TRANSPOSE] = Op("transpose", FLOAT_ARITHMETIC,
"%col = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
"%tmat = OpTranspose %type_float_mat2x2 %mat\n"
"%tcol = OpCompositeExtract %type_float_vec2 %tmat 0\n"
"%result = OpCompositeExtract %type_float %tcol 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_RETURN_VAL] = Op("ret_val", FLOAT_ARITHMETIC,
"",
"%type_test_fun = OpTypeFunction %type_float %type_float\n",
"",
"",
"%test_fun = OpFunction %type_float None %type_test_fun\n"
"%param = OpFunctionParameter %type_float\n"
"%entry = OpLabel\n"
"OpReturnValue %param\n"
"OpFunctionEnd\n",
"%result = OpFunctionCall %type_float %test_fun %arg1\n",
B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
// conversion operations that are meant to be used only for single output type (defined by the second number in name)
const char* convertSource = "%result = OpFConvert %type_float %arg1\n";
mo[OID_CONV_FROM_FP16] = Op("conv_from_fp16", FLOAT_STORAGE_ONLY, false, FP16, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_CONV_FROM_FP32] = Op("conv_from_fp32", FLOAT_STORAGE_ONLY, false, FP32, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_CONV_FROM_FP64] = Op("conv_from_fp64", FLOAT_STORAGE_ONLY, false, FP64, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
// from all operands supported by OpSpecConstantOp we can only test FConvert opcode with literals as everything
// else requires Karnel capability (OpenCL); values of literals used in SPIR-V code must be equiwalent to
// V_CONV_FROM_FP32_ARG and V_CONV_FROM_FP64_ARG so we can use same expected rounded values as for regular OpFConvert
mo[OID_SCONST_CONV_FROM_FP32_TO_FP16]
= Op("sconst_conv_from_fp32", FLOAT_ARITHMETIC, true, FP32,
"%c_arg = OpConstant %type_f32 1.22334445\n"
"%result = OpSpecConstantOp %type_f16 FConvert %c_arg\n",
"",
B_STATEMENT_USAGE_CONSTS_TYPE_FP16 | B_STATEMENT_USAGE_CONSTS_TYPE_FP32);
mo[OID_SCONST_CONV_FROM_FP64_TO_FP32]
= Op("sconst_conv_from_fp64", FLOAT_ARITHMETIC, true, FP64,
"%c_arg = OpConstant %type_f64 1.22334455\n"
"%result = OpSpecConstantOp %type_f32 FConvert %c_arg\n",
"",
B_STATEMENT_USAGE_CONSTS_TYPE_FP32 | B_STATEMENT_USAGE_CONSTS_TYPE_FP64);
mo[OID_SCONST_CONV_FROM_FP64_TO_FP16]
= Op("sconst_conv_from_fp64", FLOAT_ARITHMETIC, true, FP64,
"%c_arg = OpConstant %type_f64 1.22334445\n"
"%result = OpSpecConstantOp %type_f16 FConvert %c_arg\n",
"",
B_STATEMENT_USAGE_CONSTS_TYPE_FP16 | B_STATEMENT_USAGE_CONSTS_TYPE_FP64);
mo[OID_ADD] = Op("add", FLOAT_ARITHMETIC, "%result = OpFAdd %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SUB] = Op("sub", FLOAT_ARITHMETIC, "%result = OpFSub %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MUL] = Op("mul", FLOAT_ARITHMETIC, "%result = OpFMul %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_DIV] = Op("div", FLOAT_ARITHMETIC, "%result = OpFDiv %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_REM] = Op("rem", FLOAT_ARITHMETIC, "%result = OpFRem %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MOD] = Op("mod", FLOAT_ARITHMETIC, "%result = OpFMod %type_float %arg1 %arg2\n", B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_PHI] = Op("phi", FLOAT_ARITHMETIC,
"%comp = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
" OpSelectionMerge %comp_merge None\n"
" OpBranchConditional %comp %true_branch %false_branch\n"
"%true_branch = OpLabel\n"
" OpBranch %comp_merge\n"
"%false_branch = OpLabel\n"
" OpBranch %comp_merge\n"
"%comp_merge = OpLabel\n"
"%result = OpPhi %type_float %arg2 %true_branch %arg1 %false_branch\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SELECT] = Op("select", FLOAT_ARITHMETIC,
"%always_true = OpFOrdGreaterThan %type_bool %c_float_1 %c_float_0\n"
"%result = OpSelect %type_float %always_true %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_DOT] = Op("dot", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%vec2 = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
"%result = OpDot %type_float %vec1 %vec2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_VEC_MUL_S] = Op("vmuls", FLOAT_ARITHMETIC,
"%vec = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%tmpVec = OpVectorTimesScalar %type_float_vec2 %vec %arg2\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_VEC_MUL_M] = Op("vmulm", FLOAT_ARITHMETIC,
"%col = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
"%vec = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
"%tmpVec = OpVectorTimesMatrix %type_float_vec2 %vec %mat\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAT_MUL_S] = Op("mmuls", FLOAT_ARITHMETIC,
"%col = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
"%mulMat = OpMatrixTimesScalar %type_float_mat2x2 %mat %arg2\n"
"%extCol = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
"%result = OpCompositeExtract %type_float %extCol 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAT_MUL_V] = Op("mmulv", FLOAT_ARITHMETIC,
"%col = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
"%vec = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
"%mulVec = OpMatrixTimesVector %type_float_vec2 %mat %vec\n"
"%result = OpCompositeExtract %type_float %mulVec 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAT_MUL_M] = Op("mmulm", FLOAT_ARITHMETIC,
"%col1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat1 = OpCompositeConstruct %type_float_mat2x2 %col1 %col1\n"
"%col2 = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
"%mat2 = OpCompositeConstruct %type_float_mat2x2 %col2 %col2\n"
"%mulMat = OpMatrixTimesMatrix %type_float_mat2x2 %mat1 %mat2\n"
"%extCol = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
"%result = OpCompositeExtract %type_float %extCol 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_OUT_PROD] = Op("out_prod", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%vec2 = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
"%mulMat = OpOuterProduct %type_float_mat2x2 %vec1 %vec2\n"
"%extCol = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
"%result = OpCompositeExtract %type_float %extCol 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
// comparison operations
mo[OID_ORD_EQ] = Op("ord_eq", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_EQ] = Op("uord_eq", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ORD_NEQ] = Op("ord_neq", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdNotEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_NEQ] = Op("uord_neq", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordNotEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ORD_LS] = Op("ord_ls", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdLessThan %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_LS] = Op("uord_ls", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordLessThan %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ORD_GT] = Op("ord_gt", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_GT] = Op("uord_gt", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordGreaterThan %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ORD_LE] = Op("ord_le", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdLessThanEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_LE] = Op("uord_le", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordLessThanEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ORD_GE] = Op("ord_ge", FLOAT_ARITHMETIC,
"%boolVal = OpFOrdGreaterThanEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_UORD_GE] = Op("uord_ge", FLOAT_ARITHMETIC,
"%boolVal = OpFUnordGreaterThanEqual %type_bool %arg1 %arg2\n"
"%result = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ATAN2] = Op("atan2", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Atan2 %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_POW] = Op("pow", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Pow %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MIX] = Op("mix", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FMix %arg1 %arg2 %c_float_0_5\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FMA] = Op("fma", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Fma %arg1 %arg2 %c_float_0_5\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MIN] = Op("min", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FMin %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAX] = Op("max", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FMax %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_CLAMP] = Op("clamp", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FClamp %arg1 %arg2 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_STEP] = Op("step", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Step %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SSTEP] = Op("sstep", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 SmoothStep %arg1 %arg2 %c_float_0_5\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_DIST] = Op("distance", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Distance %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_CROSS] = Op("cross", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec3 %arg1 %arg1 %arg1\n"
"%vec2 = OpCompositeConstruct %type_float_vec3 %arg2 %arg2 %arg2\n"
"%tmpVec = OpExtInst %type_float_vec3 %std450 Cross %vec1 %vec2\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FACE_FWD] = Op("face_fwd", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FaceForward %c_float_1 %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_NMIN] = Op("nmin", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 NMin %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_NMAX] = Op("nmax", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 NMax %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_NCLAMP] = Op("nclamp", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 NClamp %arg2 %arg1 %arg2\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ROUND] = Op("round", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Round %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ROUND_EV] = Op("round_ev", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 RoundEven %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_TRUNC] = Op("trunc", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Trunc %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ABS] = Op("abs", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FAbs %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SIGN] = Op("sign", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 FSign %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FLOOR] = Op("floor", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Floor %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_CEIL] = Op("ceil", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Ceil %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FRACT] = Op("fract", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Fract %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_RADIANS] = Op("radians", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Radians %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_DEGREES] = Op("degrees", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Degrees %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SIN] = Op("sin", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Sin %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_COS] = Op("cos", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Cos %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_TAN] = Op("tan", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Tan %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ASIN] = Op("asin", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Asin %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ACOS] = Op("acos", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Acos %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ATAN] = Op("atan", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Atan %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SINH] = Op("sinh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Sinh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_COSH] = Op("cosh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Cosh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_TANH] = Op("tanh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Tanh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ASINH] = Op("asinh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Asinh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ACOSH] = Op("acosh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Acosh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_ATANH] = Op("atanh", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Atanh %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_EXP] = Op("exp", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Exp %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_LOG] = Op("log", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Log %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_EXP2] = Op("exp2", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Exp2 %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_LOG2] = Op("log2", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Log2 %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_SQRT] = Op("sqrt", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Sqrt %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_INV_SQRT] = Op("inv_sqrt", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 InverseSqrt %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MODF] = Op("modf", FLOAT_ARITHMETIC,
"",
"",
"",
"%tmpVarPtr = OpVariable %type_float_fptr Function\n",
"",
"%result = OpExtInst %type_float %std450 Modf %arg1 %tmpVarPtr\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MODF_ST] = Op("modf_st", FLOAT_ARITHMETIC,
"OpMemberDecorate %struct_ff 0 Offset ${float_width}\n"
"OpMemberDecorate %struct_ff 1 Offset ${float_width}\n",
"%struct_ff = OpTypeStruct %type_float %type_float\n"
"%struct_ff_fptr = OpTypePointer Function %struct_ff\n",
"",
"%tmpStructPtr = OpVariable %struct_ff_fptr Function\n",
"",
"%tmpStruct = OpExtInst %struct_ff %std450 ModfStruct %arg1\n"
" OpStore %tmpStructPtr %tmpStruct\n"
"%tmpLoc = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
"%result = OpLoad %type_float %tmpLoc\n",
B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FREXP] = Op("frexp", FLOAT_ARITHMETIC,
"",
"",
"",
"%tmpVarPtr = OpVariable %type_i32_fptr Function\n",
"",
"%result = OpExtInst %type_float %std450 Frexp %arg1 %tmpVarPtr\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_FREXP_ST] = Op("frexp_st", FLOAT_ARITHMETIC,
"OpMemberDecorate %struct_fi 0 Offset ${float_width}\n"
"OpMemberDecorate %struct_fi 1 Offset 32\n",
"%struct_fi = OpTypeStruct %type_float %type_i32\n"
"%struct_fi_fptr = OpTypePointer Function %struct_fi\n",
"",
"%tmpStructPtr = OpVariable %struct_fi_fptr Function\n",
"",
"%tmpStruct = OpExtInst %struct_fi %std450 FrexpStruct %arg1\n"
" OpStore %tmpStructPtr %tmpStruct\n"
"%tmpLoc = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
"%result = OpLoad %type_float %tmpLoc\n",
B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_LENGHT] = Op("length", FLOAT_ARITHMETIC,
"%result = OpExtInst %type_float %std450 Length %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_NORMALIZE] = Op("normalize", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_2\n"
"%tmpVec = OpExtInst %type_float_vec2 %std450 Normalize %vec1\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_REFLECT] = Op("reflect", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%vecN = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
"%tmpVec = OpExtInst %type_float_vec2 %std450 Reflect %vec1 %vecN\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_REFRACT] = Op("refract", FLOAT_ARITHMETIC,
"%vec1 = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%vecN = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
"%tmpVec = OpExtInst %type_float_vec2 %std450 Refract %vec1 %vecN %c_float_0_5\n"
"%result = OpCompositeExtract %type_float %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAT_DET] = Op("mat_det", FLOAT_ARITHMETIC,
"%col = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
"%result = OpExtInst %type_float %std450 Determinant %mat\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
mo[OID_MAT_INV] = Op("mat_inv", FLOAT_ARITHMETIC,
"%col1 = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_1\n"
"%col2 = OpCompositeConstruct %type_float_vec2 %c_float_1 %c_float_1\n"
"%mat = OpCompositeConstruct %type_float_mat2x2 %col1 %col2\n"
"%invMat = OpExtInst %type_float_mat2x2 %std450 MatrixInverse %mat\n"
"%extCol = OpCompositeExtract %type_float_vec2 %invMat 1\n"
"%result = OpCompositeExtract %type_float %extCol 1\n",
B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
// PackHalf2x16 is a special case as it operates on fp32 vec2 and returns unsigned int,
// the verification is done in SPIR-V code (if result is correct 1.0 will be written to SSBO)
mo[OID_PH_DENORM] = Op("ph_denorm", FLOAT_STORAGE_ONLY,
"",
"",
"%c_fp32_denorm_fp16 = OpConstant %type_f32 6.01e-5\n" // fp32 representation of fp16 denorm value
"%c_ref = OpConstant %type_u32 66061296\n",
"",
"",
"%srcVec = OpCompositeConstruct %type_f32_vec2 %c_fp32_denorm_fp16 %c_fp32_denorm_fp16\n"
"%packedInt = OpExtInst %type_u32 %std450 PackHalf2x16 %srcVec\n"
"%boolVal = OpIEqual %type_bool %c_ref %packedInt\n"
"%result = OpSelect %type_f32 %boolVal %c_f32_1 %c_f32_0\n",
B_STATEMENT_USAGE_CONSTS_TYPE_FP32 | B_STATEMENT_USAGE_COMMANDS_CONST_FP32 | B_STATEMENT_USAGE_COMMANDS_TYPE_FP32);
// UnpackHalf2x16 is a special case that operates on uint32 and returns two 32-bit floats,
// this function is tested using constants
mo[OID_UPH_DENORM] = Op("uph_denorm", FLOAT_STORAGE_ONLY,
"",
"",
"%c_u32_2_16_pack = OpConstant %type_u32 66061296\n", // == packHalf2x16(vec2(denorm))
"",
"",
"%tmpVec = OpExtInst %type_f32_vec2 %std450 UnpackHalf2x16 %c_u32_2_16_pack\n"
"%result = OpCompositeExtract %type_f32 %tmpVec 0\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FP32);
// PackDouble2x32 is a special case that operates on two uint32 and returns
// double, this function is tested using constants
mo[OID_PD_DENORM] = Op("pd_denorm", FLOAT_STORAGE_ONLY,
"",
"",
"%c_p1 = OpConstant %type_u32 0\n"
"%c_p2 = OpConstant %type_u32 262144\n", // == UnpackDouble2x32(denorm)
"",
"",
"%srcVec = OpCompositeConstruct %type_u32_vec2 %c_p1 %c_p2\n"
"%result = OpExtInst %type_f64 %std450 PackDouble2x32 %srcVec\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FP64);
// UnpackDouble2x32 is a special case as it operates only on FP64 and returns two ints,
// the verification is done in SPIR-V code (if result is correct 1.0 will be written to SSBO)
const char* unpackDouble2x32Types = "%type_bool_vec2 = OpTypeVector %type_bool 2\n";
const char* unpackDouble2x32Source = "%refVec2 = OpCompositeConstruct %type_u32_vec2 %c_p1 %c_p2\n"
"%resVec2 = OpExtInst %type_u32_vec2 %std450 UnpackDouble2x32 %arg1\n"
"%boolVec2 = OpIEqual %type_bool_vec2 %refVec2 %resVec2\n"
"%boolVal = OpAll %type_bool %boolVec2\n"
"%result = OpSelect %type_f64 %boolVal %c_f64_1 %c_f64_0\n";
mo[OID_UPD_DENORM_FLUSH] = Op("upd_denorm", FLOAT_STORAGE_ONLY, "",
unpackDouble2x32Types,
"%c_p1 = OpConstant %type_u32 0\n"
"%c_p2 = OpConstant %type_u32 0\n",
"",
"",
unpackDouble2x32Source,
B_STATEMENT_USAGE_COMMANDS_CONST_FP64 | B_STATEMENT_USAGE_COMMANDS_TYPE_FP64);
mo[OID_UPD_DENORM_PRESERVE] = Op("upd_denorm", FLOAT_STORAGE_ONLY, "",
unpackDouble2x32Types,
"%c_p1 = OpConstant %type_u32 1008\n"
"%c_p2 = OpConstant %type_u32 0\n",
"",
"",
unpackDouble2x32Source,
B_STATEMENT_USAGE_COMMANDS_CONST_FP64 | B_STATEMENT_USAGE_COMMANDS_TYPE_FP64);
mo[OID_ORTE_ROUND] = Op("orte_round", FLOAT_STORAGE_ONLY, FP32,
"OpDecorate %result FPRoundingMode RTE\n",
"",
"",
"%result = OpFConvert %type_f16 %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FP16);
mo[OID_ORTZ_ROUND] = Op("ortz_round", FLOAT_STORAGE_ONLY, FP32,
"OpDecorate %result FPRoundingMode RTZ\n",
"",
"",
"%result = OpFConvert %type_f16 %arg1\n",
B_STATEMENT_USAGE_COMMANDS_TYPE_FP16);
}
void TestCasesBuilder::build(vector<OperationTestCase>& testCases, TypeTestResultsSP typeTestResults, bool argumentsFromInput)
{
// this method constructs a list of test cases; this list is a bit different
// for every combination of float type, arguments preparation method and tested float control
testCases.reserve(750);
bool isFP16 = typeTestResults->floatType() == FP16;
// Denorm - FlushToZero - binary operations
for (size_t i = 0 ; i < typeTestResults->binaryOpFTZ.size() ; ++i)
{
const BinaryCase& binaryCase = typeTestResults->binaryOpFTZ[i];
OperationId operation = binaryCase.operationId;
testCases.push_back(OTC("denorm_op_var_flush_to_zero", B_DENORM_FLUSH, operation, V_DENORM, V_ONE, binaryCase.opVarResult));
testCases.push_back(OTC("denorm_op_denorm_flush_to_zero", B_DENORM_FLUSH, operation, V_DENORM, V_DENORM, binaryCase.opDenormResult));
testCases.push_back(OTC("denorm_op_inf_flush_to_zero", B_DENORM_FLUSH | B_ZIN_PRESERVE, operation, V_DENORM, V_INF, binaryCase.opInfResult));
testCases.push_back(OTC("denorm_op_nan_flush_to_zero", B_DENORM_FLUSH | B_ZIN_PRESERVE, operation, V_DENORM, V_NAN, binaryCase.opNanResult));
if (isFP16)
{
testCases.push_back(OTC("denorm_op_var_flush_to_zero_nostorage", B_DENORM_FLUSH, operation, V_DENORM, V_ONE, binaryCase.opVarResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_denorm_flush_to_zero_nostorage", B_DENORM_FLUSH, operation, V_DENORM, V_DENORM, binaryCase.opDenormResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_inf_flush_to_zero_nostorage", B_DENORM_FLUSH | B_ZIN_PRESERVE, operation, V_DENORM, V_INF, binaryCase.opInfResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_nan_flush_to_zero_nostorage", B_DENORM_FLUSH | B_ZIN_PRESERVE, operation, V_DENORM, V_NAN, binaryCase.opNanResult, DE_TRUE));
}
}
// Denorm - FlushToZero - unary operations
for (size_t i = 0 ; i < typeTestResults->unaryOpFTZ.size() ; ++i)
{
const UnaryCase& unaryCase = typeTestResults->unaryOpFTZ[i];
OperationId operation = unaryCase.operationId;
testCases.push_back(OTC("op_denorm_flush_to_zero", B_DENORM_FLUSH, operation, V_DENORM, V_UNUSED, unaryCase.result));
if (isFP16)
testCases.push_back(OTC("op_denorm_flush_to_zero_nostorage", B_DENORM_FLUSH, operation, V_DENORM, V_UNUSED, unaryCase.result, DE_TRUE));
}
// Denom - Preserve - binary operations
for (size_t i = 0 ; i < typeTestResults->binaryOpDenormPreserve.size() ; ++i)
{
const BinaryCase& binaryCase = typeTestResults->binaryOpDenormPreserve[i];
OperationId operation = binaryCase.operationId;
testCases.push_back(OTC("denorm_op_var_preserve", B_DENORM_PRESERVE, operation, V_DENORM, V_ONE, binaryCase.opVarResult));
testCases.push_back(OTC("denorm_op_denorm_preserve", B_DENORM_PRESERVE, operation, V_DENORM, V_DENORM, binaryCase.opDenormResult));
testCases.push_back(OTC("denorm_op_inf_preserve", B_DENORM_PRESERVE | B_ZIN_PRESERVE, operation, V_DENORM, V_INF, binaryCase.opInfResult));
testCases.push_back(OTC("denorm_op_nan_preserve", B_DENORM_PRESERVE | B_ZIN_PRESERVE, operation, V_DENORM, V_NAN, binaryCase.opNanResult));
if (isFP16)
{
testCases.push_back(OTC("denorm_op_var_preserve_nostorage", B_DENORM_PRESERVE, operation, V_DENORM, V_ONE, binaryCase.opVarResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_denorm_preserve_nostorage", B_DENORM_PRESERVE, operation, V_DENORM, V_DENORM, binaryCase.opDenormResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_inf_preserve_nostorage", B_DENORM_PRESERVE | B_ZIN_PRESERVE, operation, V_DENORM, V_INF, binaryCase.opInfResult, DE_TRUE));
testCases.push_back(OTC("denorm_op_nan_preserve_nostorage", B_DENORM_PRESERVE | B_ZIN_PRESERVE, operation, V_DENORM, V_NAN, binaryCase.opNanResult, DE_TRUE));
}
}
// Denom - Preserve - unary operations
for (size_t i = 0 ; i < typeTestResults->unaryOpDenormPreserve.size() ; ++i)
{
const UnaryCase& unaryCase = typeTestResults->unaryOpDenormPreserve[i];
OperationId operation = unaryCase.operationId;
testCases.push_back(OTC("op_denorm_preserve", B_DENORM_PRESERVE, operation, V_DENORM, V_UNUSED, unaryCase.result));
if (isFP16)
testCases.push_back(OTC("op_denorm_preserve_nostorage", B_DENORM_PRESERVE, operation, V_DENORM, V_UNUSED, unaryCase.result, DE_TRUE));
}
struct ZINCase
{
OperationId operationId;
bool supportedByFP64;
ValueId secondArgument;
ValueId preserveZeroResult;
ValueId preserveSZeroResult;
ValueId preserveInfResult;
ValueId preserveSInfResult;
ValueId preserveNanResult;
};
const ZINCase binaryOpZINPreserve[] = {
// operation fp64 second arg preserve zero preserve szero preserve inf preserve sinf preserve nan
{ OID_PHI, true, V_INF, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_SELECT, true, V_ONE, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_ADD, true, V_ZERO, V_ZERO, V_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_SUB, true, V_ZERO, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_MUL, true, V_ONE, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
};
const ZINCase unaryOpZINPreserve[] = {
// operation fp64 second arg preserve zero preserve szero preserve inf preserve sinf preserve nan
{ OID_RETURN_VAL, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_D_EXTRACT, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_D_INSERT, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_SHUFFLE, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_COMPOSITE, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_COMPOSITE_INS, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_COPY, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_TRANSPOSE, true, V_UNUSED, V_ZERO, V_MINUS_ZERO, V_INF, V_MINUS_INF, V_NAN },
{ OID_NEGATE, true, V_UNUSED, V_MINUS_ZERO, V_ZERO, V_MINUS_INF, V_INF, V_NAN },
};
bool isFP64 = typeTestResults->floatType() == FP64;
// Signed Zero Inf Nan - Preserve - binary operations
for (size_t i = 0 ; i < DE_LENGTH_OF_ARRAY(binaryOpZINPreserve) ; ++i)
{
const ZINCase& zc = binaryOpZINPreserve[i];
if (isFP64 && !zc.supportedByFP64)
continue;
testCases.push_back(OTC("zero_op_var_preserve", B_ZIN_PRESERVE, zc.operationId, V_ZERO, zc.secondArgument, zc.preserveZeroResult));
testCases.push_back(OTC("signed_zero_op_var_preserve", B_ZIN_PRESERVE, zc.operationId, V_MINUS_ZERO, zc.secondArgument, zc.preserveSZeroResult));
testCases.push_back(OTC("inf_op_var_preserve", B_ZIN_PRESERVE, zc.operationId, V_INF, zc.secondArgument, zc.preserveInfResult));
testCases.push_back(OTC("signed_inf_op_var_preserve", B_ZIN_PRESERVE, zc.operationId, V_MINUS_INF, zc.secondArgument, zc.preserveSInfResult));
testCases.push_back(OTC("nan_op_var_preserve", B_ZIN_PRESERVE, zc.operationId, V_NAN, zc.secondArgument, zc.preserveNanResult));
if (isFP16)
{
testCases.push_back(OTC("zero_op_var_preserve_nostorage", B_ZIN_PRESERVE, zc.operationId, V_ZERO, zc.secondArgument, zc.preserveZeroResult, DE_TRUE));
testCases.push_back(OTC("signed_zero_op_var_preserve_nostorage", B_ZIN_PRESERVE, zc.operationId, V_MINUS_ZERO, zc.secondArgument, zc.preserveSZeroResult, DE_TRUE));
testCases.push_back(OTC("inf_op_var_preserve_nostorage", B_ZIN_PRESERVE, zc.operationId, V_INF, zc.secondArgument, zc.preserveInfResult, DE_TRUE));
testCases.push_back(OTC("signed_inf_op_var_preserve_nostorage", B_ZIN_PRESERVE, zc.operationId, V_MINUS_INF, zc.secondArgument, zc.preserveSInfResult, DE_TRUE));
testCases.push_back(OTC("nan_op_var_preserve_nostorage", B_ZIN_PRESERVE, zc.operationId, V_NAN, zc.secondArgument, zc.preserveNanResult, DE_TRUE));
}
}
// Signed Zero Inf Nan - Preserve - unary operations
for (size_t i = 0 ; i < DE_LENGTH_OF_ARRAY(unaryOpZINPreserve) ; ++i)
{
const ZINCase& zc = unaryOpZINPreserve[i];
if (isFP64 && !zc.supportedByFP64)
continue;
testCases.push_back(OTC("op_zero_preserve", B_ZIN_PRESERVE,zc.operationId, V_ZERO, V_UNUSED, zc.preserveZeroResult));
testCases.push_back(OTC("op_signed_zero_preserve", B_ZIN_PRESERVE,zc.operationId, V_MINUS_ZERO, V_UNUSED, zc.preserveSZeroResult));
testCases.push_back(OTC("op_inf_preserve", B_ZIN_PRESERVE,zc.operationId, V_INF, V_UNUSED, zc.preserveInfResult));
testCases.push_back(OTC("op_signed_inf_preserve", B_ZIN_PRESERVE,zc.operationId, V_MINUS_INF, V_UNUSED, zc.preserveSInfResult));
testCases.push_back(OTC("op_nan_preserve", B_ZIN_PRESERVE,zc.operationId, V_NAN, V_UNUSED, zc.preserveNanResult));
if (isFP16)
{
testCases.push_back(OTC("op_zero_preserve_nostorage", B_ZIN_PRESERVE,zc.operationId, V_ZERO, V_UNUSED, zc.preserveZeroResult, DE_TRUE));
testCases.push_back(OTC("op_signed_zero_preserve_nostorage", B_ZIN_PRESERVE,zc.operationId, V_MINUS_ZERO, V_UNUSED, zc.preserveSZeroResult, DE_TRUE));
testCases.push_back(OTC("op_inf_preserve_nostorage", B_ZIN_PRESERVE,zc.operationId, V_INF, V_UNUSED, zc.preserveInfResult, DE_TRUE));
testCases.push_back(OTC("op_signed_inf_preserve_nostorage", B_ZIN_PRESERVE,zc.operationId, V_MINUS_INF, V_UNUSED, zc.preserveSInfResult, DE_TRUE));
testCases.push_back(OTC("op_nan_preserve_nostorage", B_ZIN_PRESERVE,zc.operationId, V_NAN, V_UNUSED, zc.preserveNanResult, DE_TRUE));
}
}
// comparison operations - tested differently because they return true/false
struct ComparisonCase
{
OperationId operationId;
ValueId denormPreserveResult;
};
const ComparisonCase comparisonCases[] =
{
// operation denorm
{ OID_ORD_EQ, V_ZERO },
{ OID_UORD_EQ, V_ZERO },
{ OID_ORD_NEQ, V_ONE },
{ OID_UORD_NEQ, V_ONE },
{ OID_ORD_LS, V_ONE },
{ OID_UORD_LS, V_ONE },
{ OID_ORD_GT, V_ZERO },
{ OID_UORD_GT, V_ZERO },
{ OID_ORD_LE, V_ONE },
{ OID_UORD_LE, V_ONE },
{ OID_ORD_GE, V_ZERO },
{ OID_UORD_GE, V_ZERO }
};
for (int op = 0 ; op < DE_LENGTH_OF_ARRAY(comparisonCases) ; ++op)
{
const ComparisonCase& cc = comparisonCases[op];
testCases.push_back(OTC("denorm_op_var_preserve", B_DENORM_PRESERVE, cc.operationId, V_DENORM, V_ONE, cc.denormPreserveResult));
if (isFP16)
testCases.push_back(OTC("denorm_op_var_preserve_nostorage", B_DENORM_PRESERVE, cc.operationId, V_DENORM, V_ONE, cc.denormPreserveResult, DE_TRUE));
}
if (argumentsFromInput)
{
struct RoundingModeCase
{
OperationId operationId;
ValueId arg1;
ValueId arg2;
ValueId expectedRTEResult;
ValueId expectedRTZResult;
};
const RoundingModeCase roundingCases[] =
{
{ OID_ADD, V_ADD_ARG_A, V_ADD_ARG_B, V_ADD_RTE_RESULT, V_ADD_RTZ_RESULT },
{ OID_SUB, V_SUB_ARG_A, V_SUB_ARG_B, V_SUB_RTE_RESULT, V_SUB_RTZ_RESULT },
{ OID_MUL, V_MUL_ARG_A, V_MUL_ARG_B, V_MUL_RTE_RESULT, V_MUL_RTZ_RESULT },
{ OID_DOT, V_DOT_ARG_A, V_DOT_ARG_B, V_DOT_RTE_RESULT, V_DOT_RTZ_RESULT },
// in vect/mat multiplication by scalar operations only first element of result is checked
// so argument and result values prepared for multiplication can be reused for those cases
{ OID_VEC_MUL_S, V_MUL_ARG_A, V_MUL_ARG_B, V_MUL_RTE_RESULT, V_MUL_RTZ_RESULT },
{ OID_MAT_MUL_S, V_MUL_ARG_A, V_MUL_ARG_B, V_MUL_RTE_RESULT, V_MUL_RTZ_RESULT },
{ OID_OUT_PROD, V_MUL_ARG_A, V_MUL_ARG_B, V_MUL_RTE_RESULT, V_MUL_RTZ_RESULT },
// in SPIR-V code we return first element of operation result so for following
// cases argument and result values prepared for dot product can be reused
{ OID_VEC_MUL_M, V_DOT_ARG_A, V_DOT_ARG_B, V_DOT_RTE_RESULT, V_DOT_RTZ_RESULT },
{ OID_MAT_MUL_V, V_DOT_ARG_A, V_DOT_ARG_B, V_DOT_RTE_RESULT, V_DOT_RTZ_RESULT },
{ OID_MAT_MUL_M, V_DOT_ARG_A, V_DOT_ARG_B, V_DOT_RTE_RESULT, V_DOT_RTZ_RESULT },
// conversion operations are added separately - depending on float type width
};
for (int c = 0 ; c < DE_LENGTH_OF_ARRAY(roundingCases) ; ++c)
{
const RoundingModeCase& rmc = roundingCases[c];
testCases.push_back(OTC("rounding_rte_op", B_RTE_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTEResult));
testCases.push_back(OTC("rounding_rtz_op", B_RTZ_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTZResult));
if (isFP16)
{
testCases.push_back(OTC("rounding_rte_op_nostorage", B_RTE_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTEResult, DE_TRUE));
testCases.push_back(OTC("rounding_rtz_op_nostorage", B_RTZ_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTZResult, DE_TRUE));
}
}
}
// special cases
if (typeTestResults->floatType() == FP16)
{
if (argumentsFromInput)
{
testCases.push_back(OTC("rounding_rte_conv_from_fp32", B_RTE_ROUNDING, OID_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_conv_from_fp32", B_RTZ_ROUNDING, OID_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
testCases.push_back(OTC("rounding_rte_conv_from_fp64", B_RTE_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_conv_from_fp64", B_RTZ_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp32", B_RTE_ROUNDING, OID_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp32", B_RTZ_ROUNDING, OID_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp64", B_RTE_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp64", B_RTZ_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
testCases.push_back(OTC("rounding_rte_conv_from_fp32_nostorage", B_RTE_ROUNDING, OID_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rtz_conv_from_fp32_nostorage", B_RTZ_ROUNDING, OID_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rte_conv_from_fp64_nostorage", B_RTE_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rtz_conv_from_fp64_nostorage", B_RTZ_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp32_nostorage", B_RTE_ROUNDING, OID_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp32_nostorage", B_RTZ_ROUNDING, OID_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp64_nostorage", B_RTE_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT, DE_TRUE));
testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp64_nostorage", B_RTZ_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT, DE_TRUE));
// verify that VkShaderFloatingPointRoundingModeKHR can be overridden for a given instruction by the FPRoundingMode decoration.
// FPRoundingMode decoration requires VK_KHR_16bit_storage.
testCases.push_back(OTC("rounding_rte_override", B_RTE_ROUNDING, OID_ORTZ_ROUND, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
testCases.push_back(OTC("rounding_rtz_override", B_RTZ_ROUNDING, OID_ORTE_ROUND, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
}
createUnaryTestCases(testCases, OID_CONV_FROM_FP32, V_CONV_DENORM_SMALLER, V_ZERO);
createUnaryTestCases(testCases, OID_CONV_FROM_FP64, V_CONV_DENORM_BIGGER, V_ZERO);
createUnaryTestCases(testCases, OID_CONV_FROM_FP32, V_CONV_DENORM_SMALLER, V_ZERO, DE_TRUE);
createUnaryTestCases(testCases, OID_CONV_FROM_FP64, V_CONV_DENORM_BIGGER, V_ZERO, DE_TRUE);
}
else if (typeTestResults->floatType() == FP32)
{
if (argumentsFromInput)
{
// convert from fp64 to fp32
testCases.push_back(OTC("rounding_rte_conv_from_fp64", B_RTE_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP32_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_conv_from_fp64", B_RTZ_ROUNDING, OID_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP32_RTZ_RESULT));
testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp64", B_RTE_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP32, V_UNUSED, V_UNUSED, V_CONV_TO_FP32_RTE_RESULT));
testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp64", B_RTZ_ROUNDING, OID_SCONST_CONV_FROM_FP64_TO_FP32, V_UNUSED, V_UNUSED, V_CONV_TO_FP32_RTZ_RESULT));
}
else
{
// PackHalf2x16 - verification done in SPIR-V
testCases.push_back(OTC("pack_half_denorm_preserve", B_DENORM_PRESERVE, OID_PH_DENORM, V_UNUSED, V_UNUSED, V_ONE));
// UnpackHalf2x16 - custom arguments defined as constants
testCases.push_back(OTC("upack_half_denorm_flush_to_zero", B_DENORM_FLUSH, OID_UPH_DENORM, V_UNUSED, V_UNUSED, V_ZERO));
testCases.push_back(OTC("upack_half_denorm_preserve", B_DENORM_PRESERVE, OID_UPH_DENORM, V_UNUSED, V_UNUSED, V_CONV_DENORM_SMALLER));
}
createUnaryTestCases(testCases, OID_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP32);
createUnaryTestCases(testCases, OID_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP32, DE_TRUE);
createUnaryTestCases(testCases, OID_CONV_FROM_FP64, V_CONV_DENORM_BIGGER, V_ZERO);
}
else // FP64
{
if (!argumentsFromInput)
{
// PackDouble2x32 - custom arguments defined as constants
testCases.push_back(OTC("pack_double_denorm_preserve", B_DENORM_PRESERVE, OID_PD_DENORM, V_UNUSED, V_UNUSED, V_DENORM));
// UnpackDouble2x32 - verification done in SPIR-V
testCases.push_back(OTC("upack_double_denorm_flush_to_zero", B_DENORM_FLUSH, OID_UPD_DENORM_FLUSH, V_DENORM, V_UNUSED, V_ONE));
testCases.push_back(OTC("upack_double_denorm_preserve", B_DENORM_PRESERVE, OID_UPD_DENORM_PRESERVE, V_DENORM, V_UNUSED, V_ONE));
}
createUnaryTestCases(testCases, OID_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP64);
createUnaryTestCases(testCases, OID_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP64, DE_TRUE);
createUnaryTestCases(testCases, OID_CONV_FROM_FP32, V_CONV_DENORM_BIGGER, V_ZERO_OR_FP32_DENORM_TO_FP64);
}
}
const Operation& TestCasesBuilder::getOperation(OperationId id) const
{
return m_operations.at(id);
}
void TestCasesBuilder::createUnaryTestCases(vector<OperationTestCase>& testCases, OperationId operationId, ValueId denormPreserveResult, ValueId denormFTZResult, deBool fp16WithoutStorage) const
{
if (fp16WithoutStorage)
{
// Denom - Preserve
testCases.push_back(OTC("op_denorm_preserve_nostorage", B_DENORM_PRESERVE, operationId, V_DENORM, V_UNUSED, denormPreserveResult, DE_TRUE));
// Denorm - FlushToZero
testCases.push_back(OTC("op_denorm_flush_to_zero_nostorage", B_DENORM_FLUSH, operationId, V_DENORM, V_UNUSED, denormFTZResult, DE_TRUE));
// Signed Zero Inf Nan - Preserve
testCases.push_back(OTC("op_zero_preserve_nostorage", B_ZIN_PRESERVE, operationId, V_ZERO, V_UNUSED, V_ZERO, DE_TRUE));
testCases.push_back(OTC("op_signed_zero_preserve_nostorage", B_ZIN_PRESERVE, operationId, V_MINUS_ZERO, V_UNUSED, V_MINUS_ZERO, DE_TRUE));
testCases.push_back(OTC("op_inf_preserve_nostorage", B_ZIN_PRESERVE, operationId, V_INF, V_UNUSED, V_INF, DE_TRUE));
testCases.push_back(OTC("op_nan_preserve_nostorage", B_ZIN_PRESERVE, operationId, V_NAN, V_UNUSED, V_NAN, DE_TRUE));
}
else
{
// Denom - Preserve
testCases.push_back(OTC("op_denorm_preserve", B_DENORM_PRESERVE, operationId, V_DENORM, V_UNUSED, denormPreserveResult));
// Denorm - FlushToZero
testCases.push_back(OTC("op_denorm_flush_to_zero", B_DENORM_FLUSH, operationId, V_DENORM, V_UNUSED, denormFTZResult));
// Signed Zero Inf Nan - Preserve
testCases.push_back(OTC("op_zero_preserve", B_ZIN_PRESERVE, operationId, V_ZERO, V_UNUSED, V_ZERO));
testCases.push_back(OTC("op_signed_zero_preserve", B_ZIN_PRESERVE, operationId, V_MINUS_ZERO, V_UNUSED, V_MINUS_ZERO));
testCases.push_back(OTC("op_inf_preserve", B_ZIN_PRESERVE, operationId, V_INF, V_UNUSED, V_INF));
testCases.push_back(OTC("op_nan_preserve", B_ZIN_PRESERVE, operationId, V_NAN, V_UNUSED, V_NAN));
}
}
template <typename TYPE, typename FLOAT_TYPE>
bool isZeroOrOtherValue(const TYPE& returnedFloat, ValueId secondAcceptableResult, TestLog& log)
{
if (returnedFloat.isZero() && !returnedFloat.signBit())
return true;
TypeValues<FLOAT_TYPE> typeValues;
typedef typename TYPE::StorageType SType;
typename RawConvert<FLOAT_TYPE, SType>::Value value;
value.fp = typeValues.getValue(secondAcceptableResult);
if (returnedFloat.bits() == value.ui)
return true;
log << TestLog::Message << "Expected 0 or " << toHex(value.ui)
<< " (" << value.fp << ")" << TestLog::EndMessage;
return false;
}
template <typename TYPE>
bool isAcosResultCorrect(const TYPE& returnedFloat, TestLog& log)
{
// pi/2 is result of acos(0) which in the specs is defined as equivalent to
// atan2(sqrt(1.0 - x^2), x), where atan2 has 4096 ULP, sqrt is equivalent to
// 1.0 /inversesqrt(), inversesqrt() is 2 ULP and rcp is another 2.5 ULP
double precision = 0;
const double piDiv2 = 3.14159265358979323846 / 2;
if (returnedFloat.MANTISSA_BITS == 23)
{
FloatFormat fp32Format(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE);
precision = fp32Format.ulp(piDiv2, 4096.0);
}
else
{
FloatFormat fp16Format(-14, 15, 10, true, tcu::MAYBE);
precision = fp16Format.ulp(piDiv2, 5.0);
}
if (deAbs(returnedFloat.asDouble() - piDiv2) < precision)
return true;
log << TestLog::Message << "Expected result to be in range"
<< " (" << piDiv2 - precision << ", " << piDiv2 + precision << "), got "
<< returnedFloat.asDouble() << TestLog::EndMessage;
return false;
}
template <typename TYPE>
bool isCosResultCorrect(const TYPE& returnedFloat, TestLog& log)
{
// for cos(x) with x between -pi and pi, the precision error is 2^-11 for fp32 and 2^-7 for fp16.
double precision = returnedFloat.MANTISSA_BITS == 23 ? dePow(2, -11) : dePow(2, -7);
const double expected = 1.0;
if (deAbs(returnedFloat.asDouble() - expected) < precision)
return true;
log << TestLog::Message << "Expected result to be in range"
<< " (" << expected - precision << ", " << expected + precision << "), got "
<< returnedFloat.asDouble() << TestLog::EndMessage;
return false;
}
template <typename FLOAT_TYPE>
double getFloatTypeAsDouble(FLOAT_TYPE param)
{
return param;
}
template<> double getFloatTypeAsDouble(deFloat16 param)
{
return deFloat16To64(param);
}
double getPrecisionAt(double value, float ulp, int mantissaBits)
{
if (mantissaBits == 23)
{
FloatFormat fp32Format(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE);
return fp32Format.ulp(value, ulp);
}
else if (mantissaBits == 52)
{
FloatFormat fp32Format(-1022, 1023, 52, true, tcu::MAYBE, tcu::YES, tcu::MAYBE);
return fp32Format.ulp(value, ulp);
}
else
{
DE_ASSERT(mantissaBits == 10);
FloatFormat fp16Format(-14, 15, 10, true, tcu::MAYBE);
return fp16Format.ulp(value, ulp);
}
}
template <typename TYPE, typename FLOAT_TYPE, typename REF_FUNCTION>
bool isLogResultCorrect(const TYPE& returnedFloat, FLOAT_TYPE param, REF_FUNCTION refFunction, TestLog& log)
{
if (returnedFloat.isInf() && returnedFloat.signBit())
return true;
const double expected = refFunction(getFloatTypeAsDouble(param));
const double precision = getPrecisionAt(expected, 3.0, returnedFloat.MANTISSA_BITS);
if (deAbs(returnedFloat.asDouble() - expected) < precision)
return true;
log << TestLog::Message << "Expected result to be -INF or in range"
<< " (" << expected - precision << ", " << expected + precision << "), got "
<< returnedFloat.asDouble() << TestLog::EndMessage;
return false;
}
template <typename TYPE, typename FLOAT_TYPE>
bool isInverseSqrtResultCorrect(const TYPE& returnedFloat, FLOAT_TYPE param, TestLog& log)
{
if (returnedFloat.isInf() && !returnedFloat.signBit())
return true;
const double expected = 1.0/ deSqrt(getFloatTypeAsDouble(param));
const double precision = getPrecisionAt(expected, 2.0, returnedFloat.MANTISSA_BITS);
if (deAbs(returnedFloat.asDouble() - expected) < precision)
return true;
log << TestLog::Message << "Expected result to be INF or in range"
<< " (" << expected - precision << ", " << expected + precision << "), got "
<< returnedFloat.asDouble() << TestLog::EndMessage;
return false;
}
template <typename TYPE, typename FLOAT_TYPE>
bool isSqrtResultCorrect(const TYPE& returnedFloat, FLOAT_TYPE param, TestLog& log)
{
if (returnedFloat.isZero() && !returnedFloat.signBit())
return true;
const double expected = deSqrt(getFloatTypeAsDouble(param));
const double expectedInverseSqrt = 1.0 / expected;
const double inverseSqrtPrecision = getPrecisionAt(expectedInverseSqrt, 2.0, returnedFloat.MANTISSA_BITS);
double expectedMin = deMin(1.0 / (expectedInverseSqrt - inverseSqrtPrecision), 1.0 / (expectedInverseSqrt + inverseSqrtPrecision));
double expectedMax = deMax(1.0 / (expectedInverseSqrt - inverseSqrtPrecision), 1.0 / (expectedInverseSqrt + inverseSqrtPrecision));
expectedMin -= getPrecisionAt(expectedMin, 2.5, returnedFloat.MANTISSA_BITS);
expectedMax += getPrecisionAt(expectedMax, 2.5, returnedFloat.MANTISSA_BITS);
if (returnedFloat.asDouble() >= expectedMin && returnedFloat.asDouble() <= expectedMax)
return true;
log << TestLog::Message << "Expected result to be +0 or in range"
<< " (" << expectedMin << ", " << expectedMax << "), got "
<< returnedFloat.asDouble() << TestLog::EndMessage;
return false;
}
// Function used to compare test result with expected output.
// TYPE can be Float16, Float32 or Float64.
// FLOAT_TYPE can be deFloat16, float, double.
template <typename TYPE, typename FLOAT_TYPE>
bool compareBytes(vector<deUint8>& expectedBytes, AllocationSp outputAlloc, TestLog& log)
{
const TYPE* returned = static_cast<const TYPE*>(outputAlloc->getHostPtr());
const TYPE* fValueId = reinterpret_cast<const TYPE*>(&expectedBytes.front());
// all test return single value
// Fp16 nostorage tests get their values from a deUint32 value, but we create the
// buffer with the same size for both cases: 4 bytes.
if (sizeof(TYPE) == 2u)
DE_ASSERT((expectedBytes.size() / sizeof(TYPE)) == 2);
else
DE_ASSERT((expectedBytes.size() / sizeof(TYPE)) == 1);
// during test setup we do not store expected value but id that can be used to
// retrieve actual value - this is done to handle special cases like multiple
// allowed results or epsilon checks for some cases
// note that this is workaround - this should be done by changing
// ComputerShaderCase and GraphicsShaderCase so that additional arguments can
// be passed to this verification callback
typedef typename TYPE::StorageType SType;
SType expectedInt = fValueId[0].bits();
ValueId expectedValueId = static_cast<ValueId>(expectedInt);
// something went wrong, expected value cant be V_UNUSED,
// if this is the case then test shouldn't be created at all
DE_ASSERT(expectedValueId != V_UNUSED);
TYPE returnedFloat = returned[0];
log << TestLog::Message << "Calculated result: " << toHex(returnedFloat.bits())
<< " (" << returnedFloat.asFloat() << ")" << TestLog::EndMessage;
if (expectedValueId == V_NAN)
{
if (returnedFloat.isNaN())
return true;
log << TestLog::Message << "Expected NaN" << TestLog::EndMessage;
return false;
}
if (expectedValueId == V_DENORM)
{
if (returnedFloat.isDenorm())
return true;
log << TestLog::Message << "Expected Denorm" << TestLog::EndMessage;
return false;
}
// handle multiple acceptable results cases
if (expectedValueId == V_ZERO_OR_MINUS_ZERO)
{
if (returnedFloat.isZero())
return true;
log << TestLog::Message << "Expected 0 or -0" << TestLog::EndMessage;
return false;
}
if (expectedValueId == V_ZERO_OR_ONE)
return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_ONE, log);
if ((expectedValueId == V_ZERO_OR_FP16_DENORM_TO_FP32) || (expectedValueId == V_ZERO_OR_FP16_DENORM_TO_FP64))
return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_CONV_DENORM_SMALLER, log);
if (expectedValueId == V_ZERO_OR_FP32_DENORM_TO_FP64)
return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_CONV_DENORM_BIGGER, log);
if (expectedValueId == V_ZERO_OR_DENORM_TIMES_TWO)
{
// this expected value is only needed for fp16
DE_ASSERT(returnedFloat.EXPONENT_BIAS == 15);
return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_DENORM_TIMES_TWO, log);
}
if (expectedValueId == V_MINUS_ONE_OR_CLOSE)
{
// this expected value is only needed for fp16
DE_ASSERT(returnedFloat.EXPONENT_BIAS == 15);
typename TYPE::StorageType returnedValue = returnedFloat.bits();
return (returnedValue == 0xbc00) || (returnedValue == 0xbbff);
}
// handle trigonometric operations precision errors
if (expectedValueId == V_TRIG_ONE)
return isCosResultCorrect<TYPE>(returnedFloat, log);
// handle acos(0) case
if (expectedValueId == V_PI_DIV_2)
return isAcosResultCorrect<TYPE>(returnedFloat, log);
TypeValues<FLOAT_TYPE> typeValues;
if (expectedValueId == V_MINUS_INF_OR_LOG_DENORM)
return isLogResultCorrect<TYPE>(returnedFloat, typeValues.getValue(V_DENORM), deLog, log);
if (expectedValueId == V_MINUS_INF_OR_LOG2_DENORM)
return isLogResultCorrect<TYPE>(returnedFloat, typeValues.getValue(V_DENORM), deLog2, log);
if (expectedValueId == V_ZERO_OR_SQRT_DENORM)
return isSqrtResultCorrect<TYPE>(returnedFloat, typeValues.getValue(V_DENORM), log);
if (expectedValueId == V_INF_OR_INV_SQRT_DENORM)
return isInverseSqrtResultCorrect<TYPE>(returnedFloat, typeValues.getValue(V_DENORM), log);
typename RawConvert<FLOAT_TYPE, SType>::Value value;
value.fp = typeValues.getValue(expectedValueId);
if (returnedFloat.bits() == value.ui)
return true;
log << TestLog::Message << "Expected " << toHex(value.ui)
<< " (" << value.fp << ")" << TestLog::EndMessage;
return false;
}
template <typename TYPE, typename FLOAT_TYPE>
bool checkFloats (const vector<Resource>& ,
const vector<AllocationSp>& outputAllocs,
const vector<Resource>& expectedOutputs,
TestLog& log)
{
if (outputAllocs.size() != expectedOutputs.size())
return false;
for (deUint32 outputNdx = 0; outputNdx < outputAllocs.size(); ++outputNdx)
{
vector<deUint8> expectedBytes;
expectedOutputs[outputNdx].getBytes(expectedBytes);
if (!compareBytes<TYPE, FLOAT_TYPE>(expectedBytes, outputAllocs[outputNdx], log))
return false;
}
return true;
}
bool checkMixedFloats (const vector<Resource>& ,
const vector<AllocationSp>& outputAllocs,
const vector<Resource>& expectedOutputs,
TestLog& log)