enum
{
- VK_FALSE = 0,
- VK_TRUE = 1
+ VK_FALSE = 0,
+ VK_TRUE = 1,
+ VK_WHOLE_SIZE = (~0ULL),
};
typedef VKAPI_ATTR void (VKAPI_CALL* PFN_vkVoidFunction) (void);
add_subdirectory(binding_model)
add_subdirectory(spirv_assembly)
add_subdirectory(shaderrender)
+add_subdirectory(shaderexecutor)
add_subdirectory(memory)
include_directories(
binding_model
spirv_assembly
shaderrender
+ shaderexecutor
memory
)
deqp-vk-binding-model
deqp-vk-spirv-assembly
deqp-vk-shaderrender
+ deqp-vk-shaderexecutor
deqp-vk-memory
)
--- /dev/null
+include_directories(..)
+
+set(DEQP_VK_SHADEREXECUTOR_SRCS
+ vktShaderExecutor.cpp
+ vktShaderExecutor.hpp
+ vktShaderExecutorTests.cpp
+ vktShaderExecutorTests.hpp
+
+ vktShaderCommonFunctionTests.cpp
+ vktShaderCommonFunctionTests.hpp
+ vktShaderIntegerFunctionTests.cpp
+ vktShaderIntegerFunctionTests.hpp
+ vktShaderPackingFunctionTests.cpp
+ vktShaderPackingFunctionTests.hpp
+)
+
+set(DEQP_VK_SHADEREXECUTOR_LIBS
+ tcutil
+ vkutil
+)
+
+add_library(deqp-vk-shaderexecutor STATIC ${DEQP_VK_SHADEREXECUTOR_SRCS})
+target_link_libraries(deqp-vk-shaderexecutor ${DEQP_VK_SHADEREXECUTOR_LIBS})
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Common built-in function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "vktShaderCommonFunctionTests.hpp"
+#include "vktShaderExecutor.hpp"
+#include "gluContextInfo.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuFloat.hpp"
+#include "tcuInterval.hpp"
+#include "tcuFloatFormat.hpp"
+#include "deRandom.hpp"
+#include "deMath.h"
+#include "deString.h"
+#include "deArrayUtil.hpp"
+#include "deSharedPtr.hpp"
+
+namespace vkt
+{
+
+namespace shaderexecutor
+{
+
+
+using std::vector;
+using std::string;
+using tcu::TestLog;
+
+using tcu::Vec2;
+using tcu::Vec3;
+using tcu::Vec4;
+using tcu::IVec2;
+using tcu::IVec3;
+using tcu::IVec4;
+
+namespace
+{
+
+// Utilities
+
+template<typename T, int Size>
+struct VecArrayAccess
+{
+public:
+ VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
+ ~VecArrayAccess (void) {}
+
+ const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
+ tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
+
+private:
+ tcu::Vector<T, Size>* m_array;
+};
+
+template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue);
+template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); }
+template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); }
+
+template<typename T, int Size>
+inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
+{
+ tcu::Vector<T, Size> res;
+ for (int ndx = 0; ndx < Size; ndx++)
+ res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
+ return res;
+}
+
+template<typename T, int Size>
+static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0)
+{
+ VecArrayAccess<T, Size> access(dst);
+ for (int ndx = 0; ndx < numValues; ndx++)
+ access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
+}
+
+template<typename T>
+static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
+{
+ T* typedPtr = (T*)dst;
+ for (int ndx = 0; ndx < numValues; ndx++)
+ typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
+}
+
+inline int numBitsLostInOp (float input, float output)
+{
+ const int inExp = tcu::Float32(input).exponent();
+ const int outExp = tcu::Float32(output).exponent();
+
+ return de::max(0, inExp-outExp); // Lost due to mantissa shift.
+}
+
+inline deUint32 getUlpDiff (float a, float b)
+{
+ const deUint32 aBits = tcu::Float32(a).bits();
+ const deUint32 bBits = tcu::Float32(b).bits();
+ return aBits > bBits ? aBits - bBits : bBits - aBits;
+}
+
+inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
+{
+ if (tcu::Float32(a).isZero())
+ return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
+ else if (tcu::Float32(b).isZero())
+ return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
+ else
+ return getUlpDiff(a, b);
+}
+
+inline bool supportsSignedZero (glu::Precision precision)
+{
+ // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
+ // as it is very widely supported.
+ return precision == glu::PRECISION_HIGHP;
+}
+
+inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
+{
+ const int exp = tcu::Float32(value).exponent();
+ return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
+}
+
+inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
+{
+ const int numGarbageBits = 23-numAccurateBits;
+ const deUint32 mask = (1u<<numGarbageBits)-1u;
+
+ return mask;
+}
+
+inline float getEpsFromBits (float value, int numAccurateBits)
+{
+ return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
+}
+
+static int getMinMantissaBits (glu::Precision precision)
+{
+ const int bits[] =
+ {
+ 7, // lowp
+ 10, // mediump
+ 23 // highp
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
+ return bits[precision];
+}
+
+static int getMaxNormalizedValueExponent (glu::Precision precision)
+{
+ const int exponent[] =
+ {
+ 0, // lowp
+ 13, // mediump
+ 127 // highp
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
+ return exponent[precision];
+}
+
+static int getMinNormalizedValueExponent (glu::Precision precision)
+{
+ const int exponent[] =
+ {
+ -7, // lowp
+ -13, // mediump
+ -126 // highp
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
+ return exponent[precision];
+}
+
+static float makeFloatRepresentable (float f, glu::Precision precision)
+{
+ if (precision == glu::PRECISION_HIGHP)
+ {
+ // \note: assuming f is not extended-precision
+ return f;
+ }
+ else
+ {
+ const int numMantissaBits = getMinMantissaBits(precision);
+ const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
+ const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
+ const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
+ const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
+ const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
+
+ // if zero is not required to be representable, use smallest positive non-subnormal value
+ const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f);
+
+ const tcu::Float32 float32Representation (f);
+
+ if (float32Representation.exponent() < minNormalizedValueExponent)
+ {
+ // flush too small values to zero
+ return zeroValue;
+ }
+ else if (float32Representation.exponent() > maxNormalizedValueExponent)
+ {
+ // clamp too large values
+ return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
+ }
+ else
+ {
+ // remove unrepresentable mantissa bits
+ const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(),
+ float32Representation.exponent(),
+ float32Representation.mantissaBits() & representableMantissaMask));
+
+ return targetRepresentation.asFloat();
+ }
+ }
+}
+
+static vector<int> getScalarSizes (const vector<Symbol>& symbols)
+{
+ vector<int> sizes(symbols.size());
+ for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
+ sizes[ndx] = symbols[ndx].varType.getScalarSize();
+ return sizes;
+}
+
+static int computeTotalScalarSize (const vector<Symbol>& symbols)
+{
+ int totalSize = 0;
+ for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
+ totalSize += sym->varType.getScalarSize();
+ return totalSize;
+}
+
+static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
+{
+ vector<void*> pointers (symbols.size());
+ int curScalarOffset = 0;
+
+ for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
+ {
+ const Symbol& var = symbols[varNdx];
+ const int scalarSize = var.varType.getScalarSize();
+
+ // Uses planar layout as input/output specs do not support strides.
+ pointers[varNdx] = &data[curScalarOffset];
+ curScalarOffset += scalarSize*numValues;
+ }
+
+ DE_ASSERT(curScalarOffset == (int)data.size());
+
+ return pointers;
+}
+
+// \todo [2013-08-08 pyry] Make generic utility and move to glu?
+
+struct HexFloat
+{
+ const float value;
+ HexFloat (const float value_) : value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const HexFloat& v)
+{
+ return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
+}
+
+struct HexBool
+{
+ const deUint32 value;
+ HexBool (const deUint32 value_) : value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const HexBool& v)
+{
+ return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
+}
+
+struct VarValue
+{
+ const glu::VarType& type;
+ const void* value;
+
+ VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
+{
+ DE_ASSERT(varValue.type.isBasicType());
+
+ const glu::DataType basicType = varValue.type.getBasicType();
+ const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
+ const int numComponents = glu::getDataTypeScalarSize(basicType);
+
+ if (numComponents > 1)
+ str << glu::getDataTypeName(basicType) << "(";
+
+ for (int compNdx = 0; compNdx < numComponents; compNdx++)
+ {
+ if (compNdx != 0)
+ str << ", ";
+
+ switch (scalarType)
+ {
+ case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
+ case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
+ case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
+ case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
+
+ default:
+ DE_ASSERT(false);
+ }
+ }
+
+ if (numComponents > 1)
+ str << ")";
+
+ return str;
+}
+
+static const char* getPrecisionPostfix (glu::Precision precision)
+{
+ static const char* s_postfix[] =
+ {
+ "_lowp",
+ "_mediump",
+ "_highp"
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[precision];
+}
+
+static const char* getShaderTypePostfix (glu::ShaderType shaderType)
+{
+ static const char* s_postfix[] =
+ {
+ "_vertex",
+ "_fragment",
+ "_geometry",
+ "_tess_control",
+ "_tess_eval",
+ "_compute"
+ };
+ DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[shaderType];
+}
+
+static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+{
+ return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
+}
+
+static inline void frexp (float in, float* significand, int* exponent)
+{
+ const tcu::Float32 fpValue(in);
+
+ if (!fpValue.isZero())
+ {
+ // Construct float that has exactly the mantissa, and exponent of -1.
+ *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
+ *exponent = fpValue.exponent()+1;
+ }
+ else
+ {
+ *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
+ *exponent = 0;
+ }
+}
+
+static inline float ldexp (float significand, int exponent)
+{
+ const tcu::Float32 mant(significand);
+
+ if (exponent == 0 && mant.isZero())
+ {
+ return mant.sign() < 0 ? -0.0f : 0.0f;
+ }
+ else
+ {
+ return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
+ }
+}
+
+template<class TestClass>
+static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
+{
+ tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
+ parent->addChild(group);
+
+ const glu::DataType scalarTypes[] =
+ {
+ glu::TYPE_FLOAT,
+ glu::TYPE_INT,
+ glu::TYPE_UINT
+ };
+
+ for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
+ {
+ const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
+
+ if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
+ (!intTypes && scalarType == glu::TYPE_INT) ||
+ (!uintTypes && scalarType == glu::TYPE_UINT))
+ continue;
+
+ for (int vecSize = 1; vecSize <= 4; vecSize++)
+ {
+ for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
+ {
+ for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
+ {
+ if (shaderBits & (1<<shaderTypeNdx))
+ group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
+ }
+ }
+ }
+ }
+}
+
+// CommonFunctionCase
+
+class CommonFunctionCase : public TestCase
+{
+public:
+ CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
+ ~CommonFunctionCase (void);
+ virtual void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ m_executor->setShaderSources(programCollection);
+ }
+
+ virtual TestInstance* createInstance (Context& context) const = 0;
+
+ void init (void);
+
+protected:
+ CommonFunctionCase (const CommonFunctionCase& other);
+ CommonFunctionCase& operator= (const CommonFunctionCase& other);
+
+ const glu::ShaderType m_shaderType;
+ ShaderSpec m_spec;
+ const int m_numValues;
+ de::MovePtr<ShaderExecutor> m_executor;
+};
+
+CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
+ : TestCase (testCtx, name, description)
+ , m_shaderType (shaderType)
+ , m_numValues (100)
+ , m_executor (DE_NULL)
+{
+}
+
+CommonFunctionCase::~CommonFunctionCase (void)
+{
+}
+
+void CommonFunctionCase::init (void)
+{
+ DE_ASSERT(!m_executor);
+
+ m_executor = de::MovePtr<ShaderExecutor>(createExecutor(m_shaderType, m_spec));
+ m_testCtx.getLog() << *m_executor;
+}
+
+// CommonFunctionTestInstance
+
+class CommonFunctionTestInstance : public TestInstance
+{
+public:
+ CommonFunctionTestInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : TestInstance (context)
+ , m_shaderType (shaderType)
+ , m_spec (spec)
+ , m_numValues (numValues)
+ , m_name (name)
+ , m_executor (executor)
+ {
+ }
+ virtual tcu::TestStatus iterate (void);
+
+protected:
+ virtual void getInputValues (int numValues, void* const* values) const = 0;
+ virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
+
+ const glu::ShaderType m_shaderType;
+ ShaderSpec m_spec;
+ const int m_numValues;
+
+ const char* m_name;
+
+ std::ostringstream m_failMsg; //!< Comparison failure help message.
+
+ ShaderExecutor& m_executor;
+};
+
+tcu::TestStatus CommonFunctionTestInstance::iterate (void)
+{
+ const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
+ const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
+ vector<deUint32> inputData (numInputScalars * m_numValues);
+ vector<deUint32> outputData (numOutputScalars * m_numValues);
+ const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
+ const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
+
+ // Initialize input data.
+ getInputValues(m_numValues, &inputPointers[0]);
+
+ // Execute shader.
+ m_executor.execute(m_context, m_numValues, &inputPointers[0], &outputPointers[0]);
+
+ // Compare results.
+ {
+ const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
+ const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
+ vector<void*> curInputPtr (inputPointers.size());
+ vector<void*> curOutputPtr (outputPointers.size());
+ int numFailed = 0;
+ tcu::TestContext& testCtx = m_context.getTestContext();
+
+ for (int valNdx = 0; valNdx < m_numValues; valNdx++)
+ {
+ // Set up pointers for comparison.
+ for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
+ curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
+
+ for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
+ curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
+
+ if (!compare(&curInputPtr[0], &curOutputPtr[0]))
+ {
+ // \todo [2013-08-08 pyry] We probably want to log reference value as well?
+
+ testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
+
+ testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
+ for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
+ testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
+ << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
+ << TestLog::EndMessage;
+
+ testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
+ for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
+ testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
+ << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
+ << TestLog::EndMessage;
+
+ m_failMsg.str("");
+ m_failMsg.clear();
+ numFailed += 1;
+ }
+ }
+
+ testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+ }
+}
+
+// Test cases
+
+class AbsCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ AbsCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 floatRanges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+ const IVec2 intRanges[] =
+ {
+ IVec2(-(1<<7)+1, (1<<7)-1),
+ IVec2(-(1<<15)+1, (1<<15)-1),
+ IVec2(0x80000001, 0x7fffffff)
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0x235facu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (glu::isDataTypeFloatOrVec(type))
+ fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
+ else
+ fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (glu::isDataTypeFloatOrVec(type))
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref0 = de::abs(in0);
+ const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
+
+ if (ulpDiff0 > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const int in0 = ((const int*)inputs[0])[compNdx];
+ const int out0 = ((const int*)outputs[0])[compNdx];
+ const int ref0 = de::abs(in0);
+
+ if (out0 != ref0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << ref0;
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class AbsCase : public CommonFunctionCase
+{
+public:
+ AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = abs(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new AbsCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class SignCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ SignCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 floatRanges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
+ Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
+ };
+ const IVec2 intRanges[] =
+ {
+ IVec2(-(1<<7), (1<<7)-1),
+ IVec2(-(1<<15), (1<<15)-1),
+ IVec2(0x80000000, 0x7fffffff)
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0x324u);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (glu::isDataTypeFloatOrVec(type))
+ {
+ // Special cases.
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, 0.0f);
+ fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
+ }
+ else
+ {
+ std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
+ std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
+ std::fill((int*)values[0], (int*)values[0] + scalarSize, 0);
+ fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (glu::isDataTypeFloatOrVec(type))
+ {
+ // Both highp and mediump should be able to represent -1, 0, and +1 exactly
+ const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref0 = in0 < 0.0f ? -1.0f :
+ in0 > 0.0f ? +1.0f : 0.0f;
+ const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
+
+ if (ulpDiff0 > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const int in0 = ((const int*)inputs[0])[compNdx];
+ const int out0 = ((const int*)outputs[0])[compNdx];
+ const int ref0 = in0 < 0 ? -1 :
+ in0 > 0 ? +1 : 0;
+
+ if (out0 != ref0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << ref0;
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class SignCase : public CommonFunctionCase
+{
+public:
+ SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = sign(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new SignCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+static float roundEven (float v)
+{
+ const float q = deFloatFrac(v);
+ const int truncated = int(v-q);
+ const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
+ (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
+ truncated; // Rounded down
+
+ return float(rounded);
+}
+
+class RoundEvenCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ RoundEvenCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ int numSpecialCases = 0;
+
+ // Special cases.
+ if (precision != glu::PRECISION_LOWP)
+ {
+ DE_ASSERT(numValues >= 20);
+ for (int ndx = 0; ndx < 20; ndx++)
+ {
+ const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
+ numSpecialCases += 1;
+ }
+ }
+
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool hasSignedZero = supportsSignedZero(precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ // Require exact rounding result.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref = roundEven(in0);
+
+ const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int minRes = int(roundEven(in0-eps));
+ const int maxRes = int(roundEven(in0+eps));
+ bool anyOk = false;
+
+ for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
+ {
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
+
+ if (ulpDiff <= maxUlpDiff)
+ {
+ anyOk = true;
+ break;
+ }
+ }
+
+ if (!anyOk)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class RoundEvenCase : public CommonFunctionCase
+{
+public:
+ RoundEvenCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = roundEven(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new RoundEvenCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class ModfCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ ModfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool hasZeroSign = supportsSignedZero(precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ const int mantissaBits = getMinMantissaBits(precision);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float out1 = ((const float*)outputs[1])[compNdx];
+
+ const float refOut1 = float(int(in0));
+ const float refOut0 = in0 - refOut1;
+
+ const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
+
+ const float resSum = out0 + out1;
+
+ const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
+
+ if (ulpDiff > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
+ << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class ModfCase : public CommonFunctionCase
+{
+public:
+ ModfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = modf(in0, out1);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new ModfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class IsnanCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ IsnanCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
+
+ for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
+ {
+ const bool isNan = rnd.getFloat() > 0.3f;
+ const bool isInf = !isNan && rnd.getFloat() > 0.4f;
+ const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
+ const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
+ const deUint32 sign = rnd.getUint32() & 0x1u;
+ const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
+
+ DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
+
+ ((deUint32*)values[0])[valNdx] = value;
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP)
+ {
+ // Only highp is required to support inf/nan
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
+ const bool ref = tcu::Float32(in0).isNaN();
+
+ if (out0 != ref)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
+ return false;
+ }
+ }
+ }
+ else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
+ {
+ // NaN support is optional, check that inputs that are not NaN don't result in true.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
+ const bool ref = tcu::Float32(in0).isNaN();
+
+ if (!ref && out0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class IsnanCase : public CommonFunctionCase
+{
+public:
+ IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
+ {
+ DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
+
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
+ m_spec.source = "out0 = isnan(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new IsnanCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class IsinfCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ IsinfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
+
+ for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
+ {
+ const bool isInf = rnd.getFloat() > 0.3f;
+ const bool isNan = !isInf && rnd.getFloat() > 0.4f;
+ const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
+ const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
+ const deUint32 sign = rnd.getUint32() & 0x1u;
+ const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
+
+ DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
+
+ ((deUint32*)values[0])[valNdx] = value;
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP)
+ {
+ // Only highp is required to support inf/nan
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
+ const bool ref = tcu::Float32(in0).isInf();
+
+ if (out0 != ref)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
+ return false;
+ }
+ }
+ }
+ else if (precision == glu::PRECISION_MEDIUMP)
+ {
+ // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
+ const bool ref = tcu::Float16(in0).isInf();
+
+ if (!ref && out0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
+ return false;
+ }
+ }
+ }
+ // else: no verification can be performed
+
+ return true;
+ }
+};
+
+class IsinfCase : public CommonFunctionCase
+{
+public:
+ IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
+ {
+ DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
+
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
+ m_spec.source = "out0 = isinf(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new IsinfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ FloatBitsToUintIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0x2790au);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ const int mantissaBits = getMinMantissaBits(precision);
+ const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 refOut0 = tcu::Float32(in0).bits();
+ const int ulpDiff = de::abs((int)out0 - (int)refOut0);
+
+ if (ulpDiff > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
+ << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class FloatBitsToUintIntCase : public CommonFunctionCase
+{
+public:
+ FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
+ : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
+ m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new IsinfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FloatBitsToIntCaseInstance : public FloatBitsToUintIntCaseInstance
+{
+public:
+ FloatBitsToIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : FloatBitsToUintIntCaseInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+};
+
+class FloatBitsToIntCase : public FloatBitsToUintIntCase
+{
+public:
+ FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, true)
+ {
+ }
+
+};
+
+class FloatBitsToUintCaseInstance : public FloatBitsToUintIntCaseInstance
+{
+public:
+ FloatBitsToUintCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : FloatBitsToUintIntCaseInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+};
+
+class FloatBitsToUintCase : public FloatBitsToUintIntCase
+{
+public:
+ FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, false)
+ {
+ }
+};
+
+class BitsToFloatCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ BitsToFloatCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xbbb225u);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const Vec2 range (-1e8f, +1e8f);
+
+ // \note Filled as floats.
+ fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const deUint32 maxUlpDiff = 0;
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const deUint32 ulpDiff = getUlpDiff(in0, out0);
+
+ if (ulpDiff > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
+ << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class BitsToFloatCase : public CommonFunctionCase
+{
+public:
+ BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
+ {
+ const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
+ m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new BitsToFloatCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FloorCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ FloorCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ // Require exact result.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref = deFloatFloor(in0);
+
+ const deUint32 ulpDiff = getUlpDiff(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int minRes = int(deFloatFloor(in0-eps));
+ const int maxRes = int(deFloatFloor(in0+eps));
+ bool anyOk = false;
+
+ for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
+ {
+ const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
+
+ if (ulpDiff <= maxUlpDiff)
+ {
+ anyOk = true;
+ break;
+ }
+ }
+
+ if (!anyOk)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class FloorCase : public CommonFunctionCase
+{
+public:
+ FloorCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = floor(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new FloorCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class TruncCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ TruncCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
+ const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
+
+ // Special cases
+ for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
+ {
+ for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
+ ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
+ }
+
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ // Require exact result.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const bool isNeg = tcu::Float32(in0).sign() < 0;
+ const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
+
+ // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int minRes = int(in0-eps);
+ const int maxRes = int(in0+eps);
+ bool anyOk = false;
+
+ for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
+ {
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
+
+ if (ulpDiff <= maxUlpDiff)
+ {
+ anyOk = true;
+ break;
+ }
+ }
+
+ if (!anyOk)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class TruncCase : public CommonFunctionCase
+{
+public:
+ TruncCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = trunc(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new TruncCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class RoundCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ RoundCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ int numSpecialCases = 0;
+
+ // Special cases.
+ if (precision != glu::PRECISION_LOWP)
+ {
+ DE_ASSERT(numValues >= 10);
+ for (int ndx = 0; ndx < 10; ndx++)
+ {
+ const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
+ numSpecialCases += 1;
+ }
+ }
+
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool hasZeroSign = supportsSignedZero(precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+
+ if (deFloatFrac(in0) == 0.5f)
+ {
+ // Allow both ceil(in) and floor(in)
+ const float ref0 = deFloatFloor(in0);
+ const float ref1 = deFloatCeil(in0);
+ const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
+ const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
+
+ if (ulpDiff0 > 0 && ulpDiff1 > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
+ return false;
+ }
+ }
+ else
+ {
+ // Require exact result
+ const float ref = roundEven(in0);
+ const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int minRes = int(roundEven(in0-eps));
+ const int maxRes = int(roundEven(in0+eps));
+ bool anyOk = false;
+
+ for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
+ {
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
+
+ if (ulpDiff <= maxUlpDiff)
+ {
+ anyOk = true;
+ break;
+ }
+ }
+
+ if (!anyOk)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class RoundCase : public CommonFunctionCase
+{
+public:
+ RoundCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = round(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new RoundCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class CeilCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ CeilCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool hasZeroSign = supportsSignedZero(precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ // Require exact result.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref = deFloatCeil(in0);
+
+ const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int minRes = int(deFloatCeil(in0-eps));
+ const int maxRes = int(deFloatCeil(in0+eps));
+ bool anyOk = false;
+
+ for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
+ {
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
+
+ if (ulpDiff <= maxUlpDiff)
+ {
+ anyOk = true;
+ break;
+ }
+ }
+
+ if (!anyOk && de::inRange(0, minRes, maxRes))
+ {
+ // Allow -0 as well.
+ const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
+ anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
+ }
+
+ if (!anyOk)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class CeilCase : public CommonFunctionCase
+{
+public:
+ CeilCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = ceil(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new CeilCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FractCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ FractCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ int numSpecialCases = 0;
+
+ // Special cases.
+ if (precision != glu::PRECISION_LOWP)
+ {
+ DE_ASSERT(numValues >= 10);
+ for (int ndx = 0; ndx < 10; ndx++)
+ {
+ const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
+ std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
+ numSpecialCases += 1;
+ }
+ }
+
+ // Random cases.
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
+
+ // If precision is mediump, make sure values can be represented in fp16 exactly
+ if (precision == glu::PRECISION_MEDIUMP)
+ {
+ for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
+ ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool hasZeroSign = supportsSignedZero(precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
+ {
+ // Require exact result.
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float ref = deFloatFrac(in0);
+
+ const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > 0)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ const int mantissaBits = getMinMantissaBits(precision);
+ const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+
+ if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
+ {
+ const float ref = deFloatFrac(in0);
+ const int bitsLost = numBitsLostInOp(in0, ref);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
+
+ if (ulpDiff > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+ else
+ {
+ if (out0 >= 1.0f)
+ {
+ m_failMsg << "Expected [" << compNdx << "] < 1.0";
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+class FractCase : public CommonFunctionCase
+{
+public:
+ FractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
+ m_spec.source = "out0 = fract(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new FractCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FrexpCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ FrexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0x2790au);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ // Special cases
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
+ ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
+ ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
+ ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
+ ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
+ ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
+ ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
+ ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
+ }
+
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
+
+ // Make sure the values are representable in the target format
+ for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
+ {
+ for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
+ {
+ float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
+
+ *valuePtr = makeFloatRepresentable(*valuePtr, precision);
+ }
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const bool transitSupportsSignedZero = (m_shaderType != glu::SHADERTYPE_FRAGMENT); // executor cannot reliably transit negative zero to fragment stage
+ const bool signedZero = supportsSignedZero(precision) && transitSupportsSignedZero;
+
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const int out1 = ((const int*)outputs[1])[compNdx];
+
+ float refOut0;
+ int refOut1;
+
+ frexp(in0, &refOut0, &refOut1);
+
+ const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
+
+ if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
+ << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class FrexpCase : public CommonFunctionCase
+{
+public:
+ FrexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
+ m_spec.source = "out0 = frexp(in0, out1);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new FrexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class LdexpCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ LdexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-1e3f, 1e3f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0x2790au);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ int valueNdx = 0;
+
+ {
+ const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
+
+ DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
+ {
+ float in0;
+ int in1;
+
+ frexp(easySpecialCases[caseNdx], &in0, &in1);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
+ ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
+ }
+
+ valueNdx += 1;
+ }
+ }
+
+ {
+ // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
+ const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
+
+ DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
+ for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
+ float in0;
+ int in1;
+
+ frexp(in, &in0, &in1);
+
+ ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
+ ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
+ }
+
+ valueNdx += 1;
+ }
+ }
+
+ {
+ const int numHardRandomCases = numValues-valueNdx;
+ DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
+
+ for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const int fpExp = rnd.getInt(-126, 127);
+ const int sign = rnd.getBool() ? -1 : +1;
+ const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
+ const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
+ const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
+
+ DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
+ DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
+
+ const float out = ldexp(in0, in1);
+
+ DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
+ DE_UNREF(out);
+
+ ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
+ ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
+ }
+
+ valueNdx += 1;
+ }
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ const int mantissaBits = getMinMantissaBits(precision);
+ const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float in0 = ((const float*)inputs[0])[compNdx];
+ const int in1 = ((const int*)inputs[1])[compNdx];
+ const float out0 = ((const float*)outputs[0])[compNdx];
+ const float refOut0 = ldexp(in0, in1);
+ const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
+
+ const int inExp = tcu::Float32(in0).exponent();
+
+ if (ulpDiff > maxUlpDiff)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
+ << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class LdexpCase : public CommonFunctionCase
+{
+public:
+ LdexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
+ m_spec.source = "out0 = ldexp(in0, in1);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new LdexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FmaCaseInstance : public CommonFunctionTestInstance
+{
+public:
+ FmaCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ const Vec2 ranges[] =
+ {
+ Vec2(-2.0f, 2.0f), // lowp
+ Vec2(-127.f, 127.f), // mediump
+ Vec2(-1e7f, 1e7f) // highp
+ };
+
+ de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const float specialCases[][3] =
+ {
+ // a b c
+ { 0.0f, 0.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 0.0f, -1.0f },
+ { 1.0f, 1.0f, 0.0f },
+ { 1.0f, 1.0f, 1.0f },
+ { -1.0f, 1.0f, 0.0f },
+ { 1.0f, -1.0f, 0.0f },
+ { -1.0f, -1.0f, 0.0f },
+ { -0.0f, 1.0f, 0.0f },
+ { 1.0f, -0.0f, 0.0f }
+ };
+ const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
+
+ // Special cases
+ for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
+ {
+ for (int inputNdx = 0; inputNdx < 3; inputNdx++)
+ {
+ for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
+ ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
+ }
+ }
+
+ // Random cases.
+ {
+ const int numScalars = (numValues-numSpecialCases)*scalarSize;
+ const int offs = scalarSize*numSpecialCases;
+
+ for (int inputNdx = 0; inputNdx < 3; inputNdx++)
+ fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
+ }
+
+ // Make sure the values are representable in the target format
+ for (int inputNdx = 0; inputNdx < 3; inputNdx++)
+ {
+ for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
+ {
+ for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
+ {
+ float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
+
+ *valuePtr = makeFloatRepresentable(*valuePtr, precision);
+ }
+ }
+ }
+ }
+
+ static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
+ {
+ const tcu::FloatFormat formats[] =
+ {
+ // minExp maxExp mantissa exact, subnormals infinities NaN
+ tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
+ tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
+ tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
+ };
+ const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
+ const tcu::Interval ia = format.convert(a);
+ const tcu::Interval ib = format.convert(b);
+ const tcu::Interval ic = format.convert(c);
+ tcu::Interval prod0;
+ tcu::Interval prod1;
+ tcu::Interval prod2;
+ tcu::Interval prod3;
+ tcu::Interval prod;
+ tcu::Interval res;
+
+ TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
+ TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
+ TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
+ TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
+
+ prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
+
+ TCU_SET_INTERVAL_BOUNDS(res, tmp,
+ tmp = prod.lo() + ic.lo(),
+ tmp = prod.hi() + ic.hi());
+
+ return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const float a = ((const float*)inputs[0])[compNdx];
+ const float b = ((const float*)inputs[1])[compNdx];
+ const float c = ((const float*)inputs[2])[compNdx];
+ const float res = ((const float*)outputs[0])[compNdx];
+ const tcu::Interval ref = fma(precision, a, b, c);
+
+ if (!ref.contains(res))
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << ref;
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class FmaCase : public CommonFunctionCase
+{
+public:
+ void init (void)
+ {
+ CommonFunctionCase::init();
+ }
+
+ FmaCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
+ m_spec.source = "res = fma(a, b, c);";
+ m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new FmaCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+} // anonymous
+
+ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
+ : tcu::TestCaseGroup (testCtx, "common", "Common function tests")
+{
+}
+
+ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
+{
+}
+
+void ShaderCommonFunctionTests::init (void)
+{
+ enum
+ {
+ VS = (1<<glu::SHADERTYPE_VERTEX),
+ TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
+ TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
+ GS = (1<<glu::SHADERTYPE_GEOMETRY),
+ FS = (1<<glu::SHADERTYPE_FRAGMENT),
+ CS = (1<<glu::SHADERTYPE_COMPUTE),
+
+ ALL_SHADERS = VS|TC|TE|GS|FS|CS,
+ NEW_SHADERS = TC|TE|GS|CS,
+ };
+
+ // Float? Int? Uint? Shaders
+ addFunctionCases<AbsCase> (this, "abs", true, true, false, ALL_SHADERS);
+ addFunctionCases<SignCase> (this, "sign", true, true, false, ALL_SHADERS);
+ addFunctionCases<FloorCase> (this, "floor", true, false, false, ALL_SHADERS);
+ addFunctionCases<TruncCase> (this, "trunc", true, false, false, ALL_SHADERS);
+ addFunctionCases<RoundCase> (this, "round", true, false, false, ALL_SHADERS);
+ addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, ALL_SHADERS);
+ addFunctionCases<CeilCase> (this, "ceil", true, false, false, ALL_SHADERS);
+ addFunctionCases<FractCase> (this, "fract", true, false, false, ALL_SHADERS);
+ // mod
+ addFunctionCases<ModfCase> (this, "modf", true, false, false, ALL_SHADERS);
+ // min
+ // max
+ // clamp
+ // mix
+ // step
+ // smoothstep
+ addFunctionCases<IsnanCase> (this, "isnan", true, false, false, ALL_SHADERS);
+ addFunctionCases<IsinfCase> (this, "isinf", true, false, false, ALL_SHADERS);
+ addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, ALL_SHADERS);
+ addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, ALL_SHADERS);
+
+ addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
+ addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
+ addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
+
+ // (u)intBitsToFloat()
+ {
+ const deUint32 shaderBits = NEW_SHADERS;
+ tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
+ tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
+
+ addChild(intGroup);
+ addChild(uintGroup);
+
+ for (int vecSize = 1; vecSize < 4; vecSize++)
+ {
+ const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+ const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
+
+ for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
+ {
+ if (shaderBits & (1<<shaderType))
+ {
+ intGroup->addChild(new BitsToFloatCase(getTestContext(), intType, glu::ShaderType(shaderType)));
+ uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType, glu::ShaderType(shaderType)));
+ }
+ }
+ }
+ }
+}
+
+} // shaderexecutor
+} // vkt
--- /dev/null
+#ifndef _VKTSHADERCOMMONFUNCTIONTESTS_HPP
+#define _VKTSHADERCOMMONFUNCTIONTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Common built-in function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+// ShaderCommonFunctionTests
+
+class ShaderCommonFunctionTests : public tcu::TestCaseGroup
+{
+public:
+ ShaderCommonFunctionTests (tcu::TestContext& testCtx);
+ virtual ~ShaderCommonFunctionTests (void);
+
+ virtual void init (void);
+
+private:
+ ShaderCommonFunctionTests (const ShaderCommonFunctionTests&); // not allowed!
+ ShaderCommonFunctionTests& operator= (const ShaderCommonFunctionTests&); // not allowed!
+};
+
+} // shaderexecutor
+} // vkt
+
+#endif // _VKTSHADERCOMMONFUNCTIONTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Vulkan ShaderExecutor
+ *//*--------------------------------------------------------------------*/
+
+#include "vktShaderExecutor.hpp"
+#include <map>
+#include <sstream>
+#include <iostream>
+
+#include "tcuVector.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuTextureUtil.hpp"
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+#include "deSharedPtr.hpp"
+
+#include "vkMemUtil.hpp"
+#include "vkRef.hpp"
+#include "vkPlatform.hpp"
+#include "vkPrograms.hpp"
+#include "vkStrUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkDeviceUtil.hpp"
+
+#include "gluShaderUtil.hpp"
+
+using std::vector;
+using namespace vk;
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+namespace
+{
+
+enum
+{
+ DEFAULT_RENDER_WIDTH = 100,
+ DEFAULT_RENDER_HEIGHT = 100,
+};
+
+// Shader utilities
+
+static VkClearValue getDefaultClearColor (void)
+{
+ return makeClearValueColorF32(0.125f, 0.25f, 0.5f, 1.0f);
+}
+
+static void checkSupported (const Context& ctx, glu::ShaderType shaderType)
+{
+ const VkPhysicalDeviceFeatures& features = ctx.getDeviceFeatures();
+
+ if (shaderType == glu::SHADERTYPE_GEOMETRY && !features.geometryShader)
+ TCU_THROW(NotSupportedError, "Geometry shader type not supported by device");
+ else if (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL && !features.tessellationShader)
+ TCU_THROW(NotSupportedError, "Tessellation shader type not supported by device");
+ else if (shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION && !features.tessellationShader)
+ TCU_THROW(NotSupportedError, "Tessellation shader type not supported by device");
+}
+
+static std::string generateEmptyFragmentSource ()
+{
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+ "layout(location=0) out highp vec4 o_color;\n";
+
+ src << "void main (void)\n{\n";
+ src << " o_color = vec4(0.0);\n";
+ src << "}\n";
+
+ return src.str();
+}
+
+static std::string generatePassthroughVertexShader (const std::vector<Symbol>& inputs, const char* inputPrefix, const char* outputPrefix)
+{
+
+ std::ostringstream src;
+ int location = 0;
+
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+ "layout(location = " << location << ") in highp vec4 a_position;\n";
+
+ for (vector<Symbol>::const_iterator input = inputs.begin(); input != inputs.end(); ++input)
+ {
+ location++;
+ src << "layout(location = "<< location << ") in " << glu::declare(input->varType, inputPrefix + input->name) << ";\n"
+ << "layout(location = " << location - 1 << ") flat out " << glu::declare(input->varType, outputPrefix + input->name) << ";\n";
+ }
+
+ src << "\nvoid main (void)\n{\n"
+ << " gl_Position = a_position;\n"
+ << " gl_PointSize = 1.0;\n";
+
+ for (vector<Symbol>::const_iterator input = inputs.begin(); input != inputs.end(); ++input)
+ src << "\t" << outputPrefix << input->name << " = " << inputPrefix << input->name << ";\n";
+
+ src << "}\n";
+
+ return src.str();
+}
+
+static std::string generateVertexShader (const ShaderSpec& shaderSpec, const std::string& inputPrefix, const std::string& outputPrefix)
+{
+ DE_ASSERT(!inputPrefix.empty() && !outputPrefix.empty());
+
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ src << "layout(location = 0) in highp vec4 a_position;\n";
+
+ int locationNumber = 1;
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input, ++locationNumber)
+ src << "layout(location = " << locationNumber << ") in " << glu::declare(input->varType, inputPrefix + input->name) << ";\n";
+
+ locationNumber = 0;
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output, ++locationNumber)
+ {
+ DE_ASSERT(output->varType.isBasicType());
+
+ if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+ const glu::VarType intType (intBaseType, glu::PRECISION_HIGHP);
+
+ src << "layout(location = " << locationNumber << ") flat out " << glu::declare(intType, outputPrefix + output->name) << ";\n";
+ }
+ else
+ src << "layout(location = " << locationNumber << ") flat out " << glu::declare(output->varType, outputPrefix + output->name) << ";\n";
+ }
+
+ src << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = a_position;\n"
+ << " gl_PointSize = 1.0;\n";
+
+ // Declare & fetch local input variables
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input)
+ src << "\t" << glu::declare(input->varType, input->name) << " = " << inputPrefix << input->name << ";\n";
+
+ // Declare local output variables
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ src << "\t" << glu::declare(output->varType, output->name) << ";\n";
+
+ // Operation - indented to correct level.
+ {
+ std::istringstream opSrc (shaderSpec.source);
+ std::string line;
+
+ while (std::getline(opSrc, line))
+ src << "\t" << line << "\n";
+ }
+
+ // Assignments to outputs.
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ {
+ if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+
+ src << "\t" << outputPrefix << output->name << " = " << glu::getDataTypeName(intBaseType) << "(" << output->name << ");\n";
+ }
+ else
+ src << "\t" << outputPrefix << output->name << " = " << output->name << ";\n";
+ }
+
+ src << "}\n";
+
+ return src.str();
+}
+
+struct FragmentOutputLayout
+{
+ std::vector<const Symbol*> locationSymbols; //! Symbols by location
+ std::map<std::string, int> locationMap; //! Map from symbol name to start location
+};
+
+static void generateFragShaderOutputDecl (std::ostream& src, const ShaderSpec& shaderSpec, bool useIntOutputs, const std::map<std::string, int>& outLocationMap, const std::string& outputPrefix)
+{
+ for (int outNdx = 0; outNdx < (int)shaderSpec.outputs.size(); ++outNdx)
+ {
+ const Symbol& output = shaderSpec.outputs[outNdx];
+ const int location = de::lookup(outLocationMap, output.name);
+ const std::string outVarName = outputPrefix + output.name;
+ glu::VariableDeclaration decl (output.varType, outVarName, glu::STORAGE_OUT, glu::INTERPOLATION_LAST, glu::Layout(location));
+
+ TCU_CHECK_INTERNAL(output.varType.isBasicType());
+
+ if (useIntOutputs && glu::isDataTypeFloatOrVec(output.varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output.varType.getBasicType());
+ const glu::DataType uintBasicType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
+ const glu::VarType uintType (uintBasicType, glu::PRECISION_HIGHP);
+
+ decl.varType = uintType;
+ src << decl << ";\n";
+ }
+ else if (glu::isDataTypeBoolOrBVec(output.varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output.varType.getBasicType());
+ const glu::DataType intBasicType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+ const glu::VarType intType (intBasicType, glu::PRECISION_HIGHP);
+
+ decl.varType = intType;
+ src << decl << ";\n";
+ }
+ else if (glu::isDataTypeMatrix(output.varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeMatrixNumRows(output.varType.getBasicType());
+ const int numVecs = glu::getDataTypeMatrixNumColumns(output.varType.getBasicType());
+ const glu::DataType uintBasicType = glu::getDataTypeUintVec(vecSize);
+ const glu::VarType uintType (uintBasicType, glu::PRECISION_HIGHP);
+
+ decl.varType = uintType;
+ for (int vecNdx = 0; vecNdx < numVecs; ++vecNdx)
+ {
+ decl.name = outVarName + "_" + de::toString(vecNdx);
+ decl.layout.location = location + vecNdx;
+ src << decl << ";\n";
+ }
+ }
+ else
+ src << decl << ";\n";
+ }
+}
+
+static void generateFragShaderOutAssign (std::ostream& src, const ShaderSpec& shaderSpec, bool useIntOutputs, const std::string& valuePrefix, const std::string& outputPrefix)
+{
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ {
+ if (useIntOutputs && glu::isDataTypeFloatOrVec(output->varType.getBasicType()))
+ src << " o_" << output->name << " = floatBitsToUint(" << valuePrefix << output->name << ");\n";
+ else if (glu::isDataTypeMatrix(output->varType.getBasicType()))
+ {
+ const int numVecs = glu::getDataTypeMatrixNumColumns(output->varType.getBasicType());
+
+ for (int vecNdx = 0; vecNdx < numVecs; ++vecNdx)
+ if (useIntOutputs)
+ src << "\t" << outputPrefix << output->name << "_" << vecNdx << " = floatBitsToUint(" << valuePrefix << output->name << "[" << vecNdx << "]);\n";
+ else
+ src << "\t" << outputPrefix << output->name << "_" << vecNdx << " = " << valuePrefix << output->name << "[" << vecNdx << "];\n";
+ }
+ else if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+
+ src << "\t" << outputPrefix << output->name << " = " << glu::getDataTypeName(intBaseType) << "(" << valuePrefix << output->name << ");\n";
+ }
+ else
+ src << "\t" << outputPrefix << output->name << " = " << valuePrefix << output->name << ";\n";
+ }
+}
+
+static std::string generatePassthroughFragmentShader (const ShaderSpec& shaderSpec, bool useIntOutputs, const std::map<std::string, int>& outLocationMap, const std::string& inputPrefix, const std::string& outputPrefix)
+{
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ int locationNumber = 0;
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output, ++locationNumber)
+ {
+ if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+ const glu::VarType intType (intBaseType, glu::PRECISION_HIGHP);
+
+ src << "layout(location = " << locationNumber << ") flat in " << glu::declare(intType, inputPrefix + output->name) << ";\n";
+ }
+ else
+ src << "layout(location = " << locationNumber << ") flat in " << glu::declare(output->varType, inputPrefix + output->name) << ";\n";
+ }
+
+ generateFragShaderOutputDecl(src, shaderSpec, useIntOutputs, outLocationMap, outputPrefix);
+
+ src << "\nvoid main (void)\n{\n";
+
+ generateFragShaderOutAssign(src, shaderSpec, useIntOutputs, inputPrefix, outputPrefix);
+
+ src << "}\n";
+
+ return src.str();
+}
+
+static std::string generateGeometryShader (const ShaderSpec& shaderSpec, const std::string& inputPrefix, const std::string& outputPrefix)
+{
+ DE_ASSERT(!inputPrefix.empty() && !outputPrefix.empty());
+
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_EXT_geometry_shader : require\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ src << "layout(points) in;\n"
+ << "layout(points, max_vertices = 1) out;\n";
+
+ int locationNumber = 0;
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input, ++locationNumber)
+ src << "layout(location = " << locationNumber << ") flat in " << glu::declare(input->varType, inputPrefix + input->name) << "[];\n";
+
+ locationNumber = 0;
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output, ++locationNumber)
+ {
+ DE_ASSERT(output->varType.isBasicType());
+
+ if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+ const glu::VarType intType (intBaseType, glu::PRECISION_HIGHP);
+
+ src << "layout(location = " << locationNumber << ") flat out " << glu::declare(intType, outputPrefix + output->name) << ";\n";
+ }
+ else
+ src << "layout(location = " << locationNumber << ") flat out " << glu::declare(output->varType, outputPrefix + output->name) << ";\n";
+ }
+
+ src << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = gl_in[0].gl_Position;\n\n";
+
+ // Fetch input variables
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input)
+ src << "\t" << glu::declare(input->varType, input->name) << " = " << inputPrefix << input->name << "[0];\n";
+
+ // Declare local output variables.
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ src << "\t" << glu::declare(output->varType, output->name) << ";\n";
+
+ src << "\n";
+
+ // Operation - indented to correct level.
+ {
+ std::istringstream opSrc (shaderSpec.source);
+ std::string line;
+
+ while (std::getline(opSrc, line))
+ src << "\t" << line << "\n";
+ }
+
+ // Assignments to outputs.
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ {
+ if (glu::isDataTypeBoolOrBVec(output->varType.getBasicType()))
+ {
+ const int vecSize = glu::getDataTypeScalarSize(output->varType.getBasicType());
+ const glu::DataType intBaseType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
+
+ src << "\t" << outputPrefix << output->name << " = " << glu::getDataTypeName(intBaseType) << "(" << output->name << ");\n";
+ }
+ else
+ src << "\t" << outputPrefix << output->name << " = " << output->name << ";\n";
+ }
+
+ src << " EmitVertex();\n"
+ << " EndPrimitive();\n"
+ << "}\n";
+
+ return src.str();
+}
+
+static std::string generateFragmentShader (const ShaderSpec& shaderSpec, bool useIntOutputs, const std::map<std::string, int>& outLocationMap, const std::string& inputPrefix, const std::string& outputPrefix)
+{
+ std::ostringstream src;
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ int locationNumber = 0;
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input, ++locationNumber)
+ src << "layout(location = " << locationNumber << ") flat in " << glu::declare(input->varType, inputPrefix + input->name) << ";\n";
+
+ generateFragShaderOutputDecl(src, shaderSpec, useIntOutputs, outLocationMap, outputPrefix);
+
+ src << "\nvoid main (void)\n{\n";
+
+ // Declare & fetch local input variables
+ for (vector<Symbol>::const_iterator input = shaderSpec.inputs.begin(); input != shaderSpec.inputs.end(); ++input)
+ src << "\t" << glu::declare(input->varType, input->name) << " = " << inputPrefix << input->name << ";\n";
+
+ // Declare output variables
+ for (vector<Symbol>::const_iterator output = shaderSpec.outputs.begin(); output != shaderSpec.outputs.end(); ++output)
+ src << "\t" << glu::declare(output->varType, output->name) << ";\n";
+
+ // Operation - indented to correct level.
+ {
+ std::istringstream opSrc (shaderSpec.source);
+ std::string line;
+
+ while (std::getline(opSrc, line))
+ src << "\t" << line << "\n";
+ }
+
+ generateFragShaderOutAssign(src, shaderSpec, useIntOutputs, "", outputPrefix);
+
+ src << "}\n";
+
+ return src.str();
+}
+
+// FragmentOutExecutor
+
+class FragmentOutExecutor : public ShaderExecutor
+{
+public:
+ FragmentOutExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~FragmentOutExecutor (void);
+
+ virtual void execute (const Context& ctx,
+ int numValues,
+ const void* const* inputs,
+ void* const* outputs);
+
+protected:
+ const FragmentOutputLayout m_outputLayout;
+private:
+ void bindAttributes (const Context& ctx,
+ Allocator& memAlloc,
+ int numValues,
+ const void* const* inputs);
+
+ void addAttribute (const Context& ctx,
+ Allocator& memAlloc,
+ deUint32 bindingLocation,
+ VkFormat format,
+ deUint32 sizePerElement,
+ deUint32 count,
+ const void* dataPtr);
+ // reinit render data members
+ virtual void clearRenderData (void);
+
+ typedef de::SharedPtr<Unique<VkImage> > VkImageSp;
+ typedef de::SharedPtr<Unique<VkImageView> > VkImageViewSp;
+ typedef de::SharedPtr<Unique<VkBuffer> > VkBufferSp;
+ typedef de::SharedPtr<de::UniquePtr<Allocation> > AllocationSp;
+
+ std::vector<VkVertexInputBindingDescription> m_vertexBindingDescriptions;
+ std::vector<VkVertexInputAttributeDescription> m_vertexAttributeDescriptions;
+ std::vector<VkBufferSp> m_vertexBuffers;
+ std::vector<AllocationSp> m_vertexBufferAllocs;
+};
+
+static FragmentOutputLayout computeFragmentOutputLayout (const std::vector<Symbol>& symbols)
+{
+ FragmentOutputLayout ret;
+ int location = 0;
+
+ for (std::vector<Symbol>::const_iterator it = symbols.begin(); it != symbols.end(); ++it)
+ {
+ const int numLocations = glu::getDataTypeNumLocations(it->varType.getBasicType());
+
+ TCU_CHECK_INTERNAL(!de::contains(ret.locationMap, it->name));
+ de::insert(ret.locationMap, it->name, location);
+ location += numLocations;
+
+ for (int ndx = 0; ndx < numLocations; ++ndx)
+ ret.locationSymbols.push_back(&*it);
+ }
+
+ return ret;
+}
+
+FragmentOutExecutor::FragmentOutExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : ShaderExecutor (shaderSpec, shaderType)
+ , m_outputLayout (computeFragmentOutputLayout(m_shaderSpec.outputs))
+{
+}
+
+FragmentOutExecutor::~FragmentOutExecutor (void)
+{
+}
+
+static std::vector<tcu::Vec2> computeVertexPositions (int numValues, const tcu::IVec2& renderSize)
+{
+ std::vector<tcu::Vec2> positions(numValues);
+ for (int valNdx = 0; valNdx < numValues; valNdx++)
+ {
+ const int ix = valNdx % renderSize.x();
+ const int iy = valNdx / renderSize.x();
+ const float fx = -1.0f + 2.0f*((float(ix) + 0.5f) / float(renderSize.x()));
+ const float fy = -1.0f + 2.0f*((float(iy) + 0.5f) / float(renderSize.y()));
+
+ positions[valNdx] = tcu::Vec2(fx, fy);
+ }
+
+ return positions;
+}
+
+static tcu::TextureFormat getRenderbufferFormatForOutput (const glu::VarType& outputType, bool useIntOutputs)
+{
+ const tcu::TextureFormat::ChannelOrder channelOrderMap[] =
+ {
+ tcu::TextureFormat::R,
+ tcu::TextureFormat::RG,
+ tcu::TextureFormat::RGBA, // No RGB variants available.
+ tcu::TextureFormat::RGBA
+ };
+
+ const glu::DataType basicType = outputType.getBasicType();
+ const int numComps = glu::getDataTypeNumComponents(basicType);
+ tcu::TextureFormat::ChannelType channelType;
+
+ switch (glu::getDataTypeScalarType(basicType))
+ {
+ case glu::TYPE_UINT: channelType = tcu::TextureFormat::UNSIGNED_INT32; break;
+ case glu::TYPE_INT: channelType = tcu::TextureFormat::SIGNED_INT32; break;
+ case glu::TYPE_BOOL: channelType = tcu::TextureFormat::SIGNED_INT32; break;
+ case glu::TYPE_FLOAT: channelType = useIntOutputs ? tcu::TextureFormat::UNSIGNED_INT32 : tcu::TextureFormat::FLOAT; break;
+ default:
+ throw tcu::InternalError("Invalid output type");
+ }
+
+ DE_ASSERT(de::inRange<int>(numComps, 1, DE_LENGTH_OF_ARRAY(channelOrderMap)));
+
+ return tcu::TextureFormat(channelOrderMap[numComps-1], channelType);
+}
+
+
+static VkFormat getAttributeFormat (const glu::DataType dataType)
+{
+ switch (dataType)
+ {
+ case glu::TYPE_FLOAT: return VK_FORMAT_R32_SFLOAT;
+ case glu::TYPE_FLOAT_VEC2: return VK_FORMAT_R32G32_SFLOAT;
+ case glu::TYPE_FLOAT_VEC3: return VK_FORMAT_R32G32B32_SFLOAT;
+ case glu::TYPE_FLOAT_VEC4: return VK_FORMAT_R32G32B32A32_SFLOAT;
+
+ case glu::TYPE_INT: return VK_FORMAT_R32_SINT;
+ case glu::TYPE_INT_VEC2: return VK_FORMAT_R32G32_SINT;
+ case glu::TYPE_INT_VEC3: return VK_FORMAT_R32G32B32_SINT;
+ case glu::TYPE_INT_VEC4: return VK_FORMAT_R32G32B32A32_SINT;
+
+ case glu::TYPE_UINT: return VK_FORMAT_R32_UINT;
+ case glu::TYPE_UINT_VEC2: return VK_FORMAT_R32G32_UINT;
+ case glu::TYPE_UINT_VEC3: return VK_FORMAT_R32G32B32_UINT;
+ case glu::TYPE_UINT_VEC4: return VK_FORMAT_R32G32B32A32_UINT;
+
+ case glu::TYPE_FLOAT_MAT2: return VK_FORMAT_R32G32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT2X3: return VK_FORMAT_R32G32B32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT2X4: return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT3X2: return VK_FORMAT_R32G32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT3: return VK_FORMAT_R32G32B32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT3X4: return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT4X2: return VK_FORMAT_R32G32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT4X3: return VK_FORMAT_R32G32B32_SFLOAT;
+ case glu::TYPE_FLOAT_MAT4: return VK_FORMAT_R32G32B32A32_SFLOAT;
+ default:
+ DE_ASSERT(false);
+ return VK_FORMAT_UNDEFINED;
+ }
+}
+
+void FragmentOutExecutor::addAttribute (const Context& ctx, Allocator& memAlloc, deUint32 bindingLocation, VkFormat format, deUint32 sizePerElement, deUint32 count, const void* dataPtr)
+{
+ // Add binding specification
+ const deUint32 binding = (deUint32)m_vertexBindingDescriptions.size();
+ const VkVertexInputBindingDescription bindingDescription =
+ {
+ binding,
+ sizePerElement,
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+
+ m_vertexBindingDescriptions.push_back(bindingDescription);
+
+ // Add location and format specification
+ const VkVertexInputAttributeDescription attributeDescription =
+ {
+ bindingLocation, // deUint32 location;
+ binding, // deUint32 binding;
+ format, // VkFormat format;
+ 0u, // deUint32 offsetInBytes;
+ };
+
+ m_vertexAttributeDescriptions.push_back(attributeDescription);
+
+ // Upload data to buffer
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+ const deUint32 queueFamilyIndex = ctx.getUniversalQueueFamilyIndex();
+
+ const VkDeviceSize inputSize = sizePerElement * count;
+ const VkBufferCreateInfo vertexBufferParams =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkBufferCreateFlags flags;
+ inputSize, // VkDeviceSize size;
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
+ };
+
+ Move<VkBuffer> buffer = createBuffer(vk, vkDevice, &vertexBufferParams);
+ de::MovePtr<Allocation> alloc = memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *buffer), MemoryRequirement::HostVisible);
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *buffer, alloc->getMemory(), alloc->getOffset()));
+
+ deMemcpy(alloc->getHostPtr(), dataPtr, inputSize);
+ flushMappedMemoryRange(vk, vkDevice, alloc->getMemory(), alloc->getOffset(), inputSize);
+
+ m_vertexBuffers.push_back(de::SharedPtr<Unique<VkBuffer> >(new Unique<VkBuffer>(buffer)));
+ m_vertexBufferAllocs.push_back(de::SharedPtr<de::UniquePtr<Allocation> >(new de::UniquePtr<Allocation>(alloc)));
+}
+
+void FragmentOutExecutor::bindAttributes (const Context& ctx, Allocator& memAlloc, int numValues, const void* const* inputs)
+{
+ // Input attributes
+ for (int inputNdx = 0; inputNdx < (int)m_shaderSpec.inputs.size(); inputNdx++)
+ {
+ const Symbol& symbol = m_shaderSpec.inputs[inputNdx];
+ const void* ptr = inputs[inputNdx];
+ const glu::DataType basicType = symbol.varType.getBasicType();
+ const int vecSize = glu::getDataTypeScalarSize(basicType);
+ const VkFormat format = getAttributeFormat(basicType);
+ int elementSize = 0;
+ int numAttrsToAdd = 1;
+
+ if (glu::isDataTypeFloatOrVec(basicType))
+ elementSize = sizeof(float);
+ else if (glu::isDataTypeIntOrIVec(basicType))
+ elementSize = sizeof(int);
+ else if (glu::isDataTypeUintOrUVec(basicType))
+ elementSize = sizeof(deUint32);
+ else if (glu::isDataTypeMatrix(basicType))
+ {
+ int numRows = glu::getDataTypeMatrixNumRows(basicType);
+ int numCols = glu::getDataTypeMatrixNumColumns(basicType);
+
+ elementSize = numRows * numCols * (int)sizeof(float);
+ numAttrsToAdd = numCols;
+ }
+ else
+ DE_ASSERT(false);
+
+ // add attributes, in case of matrix every column is binded as an attribute
+ for (int attrNdx = 0; attrNdx < numAttrsToAdd; attrNdx++)
+ {
+ addAttribute(ctx, memAlloc, (deUint32)m_vertexBindingDescriptions.size(), format, elementSize * vecSize, numValues, ptr);
+ }
+ }
+}
+
+void FragmentOutExecutor::clearRenderData (void)
+{
+ m_vertexBindingDescriptions.clear();
+ m_vertexAttributeDescriptions.clear();
+ m_vertexBuffers.clear();
+ m_vertexBufferAllocs.clear();
+}
+
+void FragmentOutExecutor::execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs)
+{
+ checkSupported(ctx, m_shaderType);
+
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+ const VkQueue queue = ctx.getUniversalQueue();
+ const deUint32 queueFamilyIndex = ctx.getUniversalQueueFamilyIndex();
+ Allocator& memAlloc = ctx.getDefaultAllocator();
+
+ const deInt32 renderSizeX = de::min(static_cast<int>(DEFAULT_RENDER_WIDTH), numValues);
+ const deInt32 renderSizeY = (numValues / renderSizeX) + ((numValues % renderSizeX != 0) ? 1 : 0);
+ const tcu::IVec2 renderSize (renderSizeX, renderSizeY);
+ std::vector<tcu::Vec2> positions;
+
+ VkFormat colorFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
+
+ const bool useGeometryShader = m_shaderType == glu::SHADERTYPE_GEOMETRY;
+
+ std::vector<VkImageSp> colorImages;
+ std::vector<AllocationSp> colorImageAllocs;
+ std::vector<VkAttachmentDescription> attachments;
+ std::vector<VkClearValue> attachmentClearValues;
+ std::vector<VkImageViewSp> colorImageViews;
+
+ std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
+ std::vector<VkAttachmentReference> colorAttachmentReferences;
+
+ Move<VkRenderPass> renderPass;
+ Move<VkFramebuffer> framebuffer;
+ Move<VkPipelineLayout> pipelineLayout;
+ Move<VkPipeline> graphicsPipeline;
+
+ Move<VkShaderModule> vertexShaderModule;
+ Move<VkShaderModule> geometryShaderModule;
+ Move<VkShaderModule> fragmentShaderModule;
+
+ Move<VkCommandPool> cmdPool;
+ Move<VkCommandBuffer> cmdBuffer;
+
+ Move<VkFence> fence;
+
+ clearRenderData();
+
+ // Compute positions - 1px points are used to drive fragment shading.
+ positions = computeVertexPositions(numValues, renderSize);
+
+ // Bind attributes
+ addAttribute(ctx, memAlloc, 0u, VK_FORMAT_R32G32_SFLOAT, sizeof(tcu::Vec2), (deUint32)positions.size(), &positions[0]);
+ bindAttributes(ctx, memAlloc, numValues, inputs);
+
+ // Create color images
+ {
+ const VkImageCreateInfo colorImageParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ colorFormat, // VkFormat format;
+ { renderSize.x(), renderSize.y(), 1u }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arraySize;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+
+ const VkAttachmentDescription colorAttachmentDescription =
+ {
+ 0u, // VkAttachmentDescriptorFlags flags;
+ colorFormat, // VkFormat format;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout;
+ };
+
+ const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
+ {
+ VK_FALSE, // VkBool32 blendEnable;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor;
+ VK_BLEND_OP_ADD, // VkBlendOp blendOpColor;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor destAlphaBlendFactor;
+ VK_BLEND_OP_ADD, // VkBlendOp blendOpAlpha;
+ (VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT |
+ VK_COLOR_COMPONENT_A_BIT) // VkColorComponentFlags colorWriteMask;
+ };
+
+ for (int outNdx = 0; outNdx < (int)m_outputLayout.locationSymbols.size(); ++outNdx)
+ {
+ Move<VkImage> colorImage = createImage(vk, vkDevice, &colorImageParams);
+ colorImages.push_back(de::SharedPtr<Unique<VkImage> >(new Unique<VkImage>(colorImage)));
+ attachmentClearValues.push_back(getDefaultClearColor());
+
+ // Allocate and bind color image memory
+ {
+ de::MovePtr<Allocation> colorImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *((const VkImage*) colorImages.back().get())), MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(vkDevice, colorImages.back().get()->get(), colorImageAlloc->getMemory(), colorImageAlloc->getOffset()));
+ colorImageAllocs.push_back(de::SharedPtr<de::UniquePtr<Allocation> >(new de::UniquePtr<Allocation>(colorImageAlloc)));
+
+ attachments.push_back(colorAttachmentDescription);
+ colorBlendAttachmentStates.push_back(colorBlendAttachmentState);
+
+ const VkAttachmentReference colorAttachmentReference = {
+ (deUint32) (colorImages.size() - 1), // deUint32 attachment;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
+ };
+
+ colorAttachmentReferences.push_back(colorAttachmentReference);
+ }
+
+ // Create color attachment view
+ {
+ const VkImageViewCreateInfo colorImageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageViewCreateFlags flags;
+ colorImages.back().get()->get(), // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ colorFormat, // VkFormat format;
+ {
+ VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle r;
+ VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle g;
+ VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle b;
+ VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle a;
+ }, // VkComponentMapping components;
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
+ 0u, // deUint32 baseMipLevel;
+ 0u, // deUint32 mipLevels;
+ 0u, // deUint32 baseArraySlice;
+ 1u // deUint32 arraySize;
+ } // VkImageSubresourceRange subresourceRange;
+ };
+
+ Move<VkImageView> colorImageView = createImageView(vk, vkDevice, &colorImageViewParams);
+ colorImageViews.push_back(de::SharedPtr<Unique<VkImageView> >(new Unique<VkImageView>(colorImageView)));
+ }
+ }
+ }
+
+ // Create render pass
+ {
+ const VkSubpassDescription subpassDescription =
+ {
+ 0u, // VkSubpassDescriptionFlags flags;
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // deUint32 inputCount;
+ DE_NULL, // const VkAttachmentReference* pInputAttachments;
+ (deUint32)colorImages.size(), // deUint32 colorCount;
+ &colorAttachmentReferences[0], // const VkAttachmentReference* colorAttachments;
+ DE_NULL, // const VkAttachmentReference* resolveAttachments;
+ DE_NULL, // VkAttachmentReference depthStencilAttachment;
+ 0u, // deUint32 preserveCount;
+ DE_NULL // const VkAttachmentReference* pPreserveAttachments;
+ };
+
+ const VkRenderPassCreateInfo renderPassParams =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
+ (deUint32)attachments.size(), // deUint32 attachmentCount;
+ &attachments[0], // const VkAttachmentDescription* pAttachments;
+ 1u, // deUint32 subpassCount;
+ &subpassDescription, // const VkSubpassDescription* pSubpasses;
+ 0u, // deUint32 dependencyCount;
+ DE_NULL // const VkSubpassDependency* pDependencies;
+ };
+
+ renderPass = createRenderPass(vk, vkDevice, &renderPassParams);
+ }
+
+ // Create framebuffer
+ {
+ std::vector<VkImageView> views(colorImageViews.size());
+ for (size_t i = 0; i < colorImageViews.size(); i++)
+ {
+ views[i] = colorImageViews[i].get()->get();
+ }
+
+ const VkFramebufferCreateInfo framebufferParams =
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkFramebufferCreateFlags flags;
+ *renderPass, // VkRenderPass renderPass;
+ (deUint32)views.size(), // deUint32 attachmentCount;
+ &views[0], // const VkImageView* pAttachments;
+ (deUint32)renderSize.x(), // deUint32 width;
+ (deUint32)renderSize.y(), // deUint32 height;
+ 1u // deUint32 layers;
+ };
+
+ framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
+ }
+
+ // Create pipeline layout
+ {
+ const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ 0u, // deUint32 descriptorSetCount;
+ DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+
+ pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+ }
+
+ // Create shaders
+ {
+ vertexShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("vert"), 0);
+ fragmentShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("frag"), 0);
+
+ if (useGeometryShader)
+ {
+ geometryShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("geom"), 0);
+ }
+ }
+
+ // Create pipeline
+ {
+ std::vector<VkPipelineShaderStageCreateInfo> shaderStageParams;
+
+ const VkPipelineShaderStageCreateInfo vertexShaderStageParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage;
+ *vertexShaderModule, // VkShaderModule module;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ };
+
+ const VkPipelineShaderStageCreateInfo fragmentShaderStageParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBits stage;
+ *fragmentShaderModule, // VkShaderModule module;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ };
+
+ shaderStageParams.push_back(vertexShaderStageParams);
+ shaderStageParams.push_back(fragmentShaderStageParams);
+
+ if (useGeometryShader)
+ {
+ const VkPipelineShaderStageCreateInfo geometryShaderStageParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_GEOMETRY_BIT, // VkShaderStageFlagBits stage;
+ *geometryShaderModule, // VkShaderModule module;
+ "main", // VkShader shader;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ };
+
+ shaderStageParams.push_back(geometryShaderStageParams);
+ }
+
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
+ (deUint32)m_vertexBindingDescriptions.size(), // deUint32 bindingCount;
+ &m_vertexBindingDescriptions[0], // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ (deUint32)m_vertexAttributeDescriptions.size(), // deUint32 attributeCount;
+ &m_vertexAttributeDescriptions[0], // const VkVertexInputAttributeDescription* pvertexAttributeDescriptions;
+ };
+
+ const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineInputAssemblyStateCreateFlags)0, // VkPipelineInputAssemblyStateCreateFlags flags;
+ VK_PRIMITIVE_TOPOLOGY_POINT_LIST, // VkPrimitiveTopology topology;
+ DE_FALSE // VkBool32 primitiveRestartEnable;
+ };
+
+ const VkViewport viewport =
+ {
+ 0.0f, // float originX;
+ 0.0f, // float originY;
+ (float)renderSize.x(), // float width;
+ (float)renderSize.y(), // float height;
+ 0.0f, // float minDepth;
+ 1.0f // float maxDepth;
+ };
+
+ const VkRect2D scissor =
+ {
+ {
+ 0u, // deUint32 x;
+ 0u, // deUint32 y;
+ }, // VkOffset2D offset;
+ {
+ renderSize.x(), // deUint32 width;
+ renderSize.y(), // deUint32 height;
+ }, // VkExtent2D extent;
+ };
+
+ const VkPipelineViewportStateCreateInfo viewportStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineViewportStateCreateFlags flags;
+ 1u, // deUint32 viewportCount;
+ &viewport, // const VkViewport* pViewports;
+ 1u, // deUint32 scissorsCount;
+ &scissor // const VkRect2D* pScissors;
+ };
+
+ const VkPipelineRasterizationStateCreateInfo rasterStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineRasterizationStateCreateFlags)0u, //VkPipelineRasterizationStateCreateFlags flags;
+ VK_FALSE, // VkBool32 depthClipEnable;
+ VK_FALSE, // VkBool32 rasterizerDiscardEnable;
+ VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
+ VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
+ VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
+ VK_FALSE, // VkBool32 depthBiasEnable;
+ 0.0f, // float depthBias;
+ 0.0f, // float depthBiasClamp;
+ 0.0f, // float slopeScaledDepthBias;
+ 1.0f // float lineWidth;
+ };
+
+ const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags;
+ VK_FALSE, // VkBool32 logicOpEnable;
+ VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
+ (deUint32)colorBlendAttachmentStates.size(), // deUint32 attachmentCount;
+ &colorBlendAttachmentStates[0], // const VkPipelineColorBlendAttachmentState* pAttachments;
+ { 0.0f, 0.0f, 0.0f, 0.0f } // float blendConst[4];
+ };
+
+ const VkPipelineDynamicStateCreateInfo dynamicStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineDynamicStateCreateFlags)0, // VkPipelineDynamicStateCreateFlags flags;
+ 0u, // deUint32 dynamicStateCount;
+ DE_NULL // const VkDynamicState* pDynamicStates;
+ };
+
+ const VkGraphicsPipelineCreateInfo graphicsPipelineParams =
+ {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
+ (deUint32)shaderStageParams.size(), // deUint32 stageCount;
+ &shaderStageParams[0], // const VkPipelineShaderStageCreateInfo* pStages;
+ &vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
+ &inputAssemblyStateParams, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
+ DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState;
+ &viewportStateParams, // const VkPipelineViewportStateCreateInfo* pViewportState;
+ &rasterStateParams, // const VkPipelineRasterStateCreateInfo* pRasterState;
+ DE_NULL, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
+ DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
+ &colorBlendStateParams, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
+ &dynamicStateInfo, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
+ *pipelineLayout, // VkPipelineLayout layout;
+ *renderPass, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ 0u, // VkPipeline basePipelineHandle;
+ 0u // deInt32 basePipelineIndex;
+ };
+
+ graphicsPipeline = createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams);
+ }
+
+ // Create command pool
+ {
+ const VkCommandPoolCreateInfo cmdPoolParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, // VkCmdPoolCreateFlags flags;
+ queueFamilyIndex, // deUint32 queueFamilyIndex;
+ };
+
+ cmdPool = createCommandPool(vk, vkDevice, &cmdPoolParams);
+ }
+
+ // Create command buffer
+ {
+ const VkCommandBufferAllocateInfo cmdBufferParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *cmdPool, // VkCmdPool cmdPool;
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCmdBufferLevel level;
+ 1 // deUint32 bufferCount;
+ };
+
+ const VkCommandBufferBeginInfo cmdBufferBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkCmdBufferOptimizeFlags flags;
+ DE_NULL, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ DE_NULL, // VkFramebuffer framebuffer;
+ VK_FALSE, // VkBool32 occlusionQueryEnable;
+ (VkQueryControlFlags)0u, //VkQueryControlFlags queryFlags;
+ (VkQueryPipelineStatisticFlags)0u //VkQueryPipelineStatisticFlags pipelineStatistics;
+ };
+
+ const VkRenderPassBeginInfo renderPassBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *renderPass, // VkRenderPass renderPass;
+ *framebuffer, // VkFramebuffer framebuffer;
+ { { 0, 0 }, { renderSize.x(), renderSize.y() } }, // VkRect2D renderArea;
+ (deUint32)attachmentClearValues.size(), // deUint32 attachmentCount;
+ &attachmentClearValues[0] // const VkClearValue* pAttachmentClearValues;
+ };
+
+ cmdBuffer = allocateCommandBuffer(vk, vkDevice, &cmdBufferParams);
+
+ VK_CHECK(vk.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
+ vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+
+ const deUint32 numberOfVertexAttributes = (deUint32)m_vertexBuffers.size();
+ std::vector<VkDeviceSize> offsets(numberOfVertexAttributes, 0);
+
+ std::vector<VkBuffer> buffers(numberOfVertexAttributes);
+ for (size_t i = 0; i < numberOfVertexAttributes; i++)
+ {
+ buffers[i] = m_vertexBuffers[i].get()->get();
+ }
+
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0, numberOfVertexAttributes, &buffers[0], &offsets[0]);
+ vk.cmdDraw(*cmdBuffer, (deUint32)positions.size(), 1u, 0u, 0u);
+
+ vk.cmdEndRenderPass(*cmdBuffer);
+ VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
+ }
+
+ // Create fence
+ {
+ const VkFenceCreateInfo fenceParams =
+ {
+ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u // VkFenceCreateFlags flags;
+ };
+
+ fence = createFence(vk, vkDevice, &fenceParams);
+ }
+
+ // Execute Draw
+ {
+
+ const VkSubmitInfo submitInfo =
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType
+ DE_NULL, // pNext
+ 0u, // waitSemaphoreCount
+ DE_NULL, // pWaitSemaphores
+ 1u, // commandBufferCount
+ &cmdBuffer.get(), // pCommandBuffers
+ 0u, // signalSemaphoreCount
+ DE_NULL // pSignalSemaphores
+ };
+
+ VK_CHECK(vk.resetFences(vkDevice, 1, &fence.get()));
+ VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
+ VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), DE_TRUE, ~(0ull) /* infinity*/));
+ }
+
+ // Read back result and output
+ {
+ const VkDeviceSize imageSizeBytes = (VkDeviceSize)(4 * sizeof(deUint32) * renderSize.x() * renderSize.y());
+ const VkBufferCreateInfo readImageBufferParams =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkBufferCreateFlags flags;
+ imageSizeBytes, // VkDeviceSize size;
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ };
+
+ // constants for image copy
+
+ const VkCommandPoolCreateInfo cmdPoolParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, // VkCmdPoolCreateFlags flags;
+ queueFamilyIndex // deUint32 queueFamilyIndex;
+ };
+
+ Move<VkCommandPool> copyCmdPool = createCommandPool(vk, vkDevice, &cmdPoolParams);
+
+ const VkCommandBufferAllocateInfo cmdBufferParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *copyCmdPool, // VkCmdPool cmdPool;
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCmdBufferLevel level;
+ 1u // deUint32 bufferCount;
+ };
+
+ const VkCommandBufferBeginInfo cmdBufferBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkCmdBufferOptimizeFlags flags;
+ DE_NULL, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ DE_NULL, // VkFramebuffer framebuffer;
+ VK_FALSE, // VkBool32 occlusionQueryEnable;
+ (VkQueryControlFlags)0, // VkQueryControlFlags queryFlags;
+ (VkQueryPipelineStatisticFlags)0 // VkQueryPipelineStatisticFlags pipelineStatistics;
+
+ };
+
+ const VkBufferImageCopy copyParams =
+ {
+ 0u, // VkDeviceSize bufferOffset;
+ (deUint32)renderSize.x(), // deUint32 bufferRowLength;
+ (deUint32)renderSize.y(), // deUint32 bufferImageHeight;
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspect aspect;
+ 0u, // deUint32 mipLevel;
+ 0u, // deUint32 arraySlice;
+ 1u, // deUint32 arraySize;
+ }, // VkImageSubresource imageSubresource;
+ { 0u, 0u, 0u }, // VkOffset3D imageOffset;
+ { renderSize.x(), renderSize.y(), 1u } // VkExtent3D imageExtent;
+ };
+
+ // Read back pixels.
+ for (int outNdx = 0; outNdx < (int)m_shaderSpec.outputs.size(); ++outNdx)
+ {
+ const Symbol& output = m_shaderSpec.outputs[outNdx];
+ const int outSize = output.varType.getScalarSize();
+ const int outVecSize = glu::getDataTypeNumComponents(output.varType.getBasicType());
+ const int outNumLocs = glu::getDataTypeNumLocations(output.varType.getBasicType());
+ deUint32* dstPtrBase = static_cast<deUint32*>(outputs[outNdx]);
+ const int outLocation = de::lookup(m_outputLayout.locationMap, output.name);
+
+ for (int locNdx = 0; locNdx < outNumLocs; ++locNdx)
+ {
+ tcu::TextureLevel tmpBuf;
+ const tcu::TextureFormat format = getRenderbufferFormatForOutput(output.varType, false);
+ const tcu::TextureFormat readFormat (tcu::TextureFormat::RGBA, format.type);
+ const Unique<VkBuffer> readImageBuffer(createBuffer(vk, vkDevice, &readImageBufferParams));
+ const de::UniquePtr<Allocation> readImageBufferMemory(memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *readImageBuffer), MemoryRequirement::HostVisible));
+
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *readImageBuffer, readImageBufferMemory->getMemory(), readImageBufferMemory->getOffset()));
+
+ // Copy image to buffer
+ {
+
+ const VkSubmitInfo submitInfo =
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ DE_NULL,
+ 0u,
+ (const VkSemaphore*)DE_NULL,
+ 1u,
+ &cmdBuffer.get(),
+ 0u,
+ (const VkSemaphore*)DE_NULL,
+ };
+
+ Move<VkCommandBuffer> copyCmdBuffer = allocateCommandBuffer(vk, vkDevice, &cmdBufferParams);
+
+ VK_CHECK(vk.beginCommandBuffer(*copyCmdBuffer, &cmdBufferBeginInfo));
+ vk.cmdCopyImageToBuffer(*copyCmdBuffer, colorImages[outLocation + locNdx].get()->get(), VK_IMAGE_LAYOUT_UNDEFINED, *readImageBuffer, 1u, ©Params);
+ VK_CHECK(vk.endCommandBuffer(*copyCmdBuffer));
+
+ VK_CHECK(vk.resetFences(vkDevice, 1, &fence.get()));
+ VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
+ VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), true, ~(0ull) /* infinity */));
+ }
+
+ const VkMappedMemoryRange range =
+ {
+ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ readImageBufferMemory->getMemory(), // VkDeviceMemory mem;
+ 0, // VkDeviceSize offset;
+ imageSizeBytes, // VkDeviceSize size;
+ };
+
+ VK_CHECK(vk.invalidateMappedMemoryRanges(vkDevice, 1u, &range));
+
+ tmpBuf.setStorage(readFormat, renderSize.x(), renderSize.y());
+
+ const tcu::TextureFormat resultFormat(tcu::TextureFormat::RGBA, format.type);
+ const tcu::ConstPixelBufferAccess resultAccess(resultFormat, renderSize.x(), renderSize.y(), 1, readImageBufferMemory->getHostPtr());
+
+ tcu::copy(tmpBuf.getAccess(), resultAccess);
+
+ if (outSize == 4 && outNumLocs == 1)
+ deMemcpy(dstPtrBase, tmpBuf.getAccess().getDataPtr(), numValues * outVecSize * sizeof(deUint32));
+ else
+ {
+ for (int valNdx = 0; valNdx < numValues; valNdx++)
+ {
+ const deUint32* srcPtr = (const deUint32*)tmpBuf.getAccess().getDataPtr() + valNdx * 4;
+ deUint32* dstPtr = &dstPtrBase[outSize * valNdx + outVecSize * locNdx];
+ deMemcpy(dstPtr, srcPtr, outVecSize * sizeof(deUint32));
+ }
+ }
+ }
+ }
+ }
+}
+
+// VertexShaderExecutor
+
+class VertexShaderExecutor : public FragmentOutExecutor
+{
+public:
+ VertexShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~VertexShaderExecutor (void);
+
+ virtual void log (tcu::TestLog& dst) const { /* TODO */ (void)dst;}
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+};
+
+VertexShaderExecutor::VertexShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : FragmentOutExecutor (shaderSpec, shaderType)
+{
+}
+
+VertexShaderExecutor::~VertexShaderExecutor (void)
+{
+}
+
+void VertexShaderExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("vert") << glu::VertexSource(generateVertexShader(m_shaderSpec, "a_", "vtx_out_"));
+ /* \todo [2015-09-11 hegedusd] set useIntOutputs parameter if needed. */
+ programCollection.glslSources.add("frag") << glu::FragmentSource(generatePassthroughFragmentShader(m_shaderSpec, false, m_outputLayout.locationMap, "vtx_out_", "o_"));
+}
+
+// GeometryShaderExecutor
+
+class GeometryShaderExecutor : public FragmentOutExecutor
+{
+public:
+ GeometryShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~GeometryShaderExecutor (void);
+
+ virtual void log (tcu::TestLog& dst) const { /* TODO */ (void)dst; }
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+};
+
+GeometryShaderExecutor::GeometryShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : FragmentOutExecutor (shaderSpec, shaderType)
+{
+}
+
+GeometryShaderExecutor::~GeometryShaderExecutor (void)
+{
+}
+
+void GeometryShaderExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("vert") << glu::VertexSource(generatePassthroughVertexShader(m_shaderSpec.inputs, "a_", "vtx_out_"));
+
+ programCollection.glslSources.add("geom") << glu::GeometrySource(generateGeometryShader(m_shaderSpec, "vtx_out_", "geom_out_"));
+
+ /* \todo [2015-09-18 rsipka] set useIntOutputs parameter if needed. */
+ programCollection.glslSources.add("frag") << glu::FragmentSource(generatePassthroughFragmentShader(m_shaderSpec, false, m_outputLayout.locationMap, "geom_out_", "o_"));
+
+}
+
+// FragmentShaderExecutor
+
+class FragmentShaderExecutor : public FragmentOutExecutor
+{
+public:
+ FragmentShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~FragmentShaderExecutor (void);
+
+ virtual void log (tcu::TestLog& dst) const { /* TODO */ (void)dst; }
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+};
+
+FragmentShaderExecutor::FragmentShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : FragmentOutExecutor (shaderSpec, shaderType)
+{
+}
+
+FragmentShaderExecutor::~FragmentShaderExecutor (void)
+{
+}
+
+void FragmentShaderExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("vert") << glu::VertexSource(generatePassthroughVertexShader(m_shaderSpec.inputs, "a_", "vtx_out_"));
+ /* \todo [2015-09-11 hegedusd] set useIntOutputs parameter if needed. */
+ programCollection.glslSources.add("frag") << glu::FragmentSource(generateFragmentShader(m_shaderSpec, false, m_outputLayout.locationMap, "vtx_out_", "o_"));
+}
+
+// Shared utilities for compute and tess executors
+
+static deUint32 getVecStd430ByteAlignment (glu::DataType type)
+{
+ switch (glu::getDataTypeScalarSize(type))
+ {
+ case 1: return 4u;
+ case 2: return 8u;
+ case 3: return 16u;
+ case 4: return 16u;
+ default:
+ DE_ASSERT(false);
+ return 0u;
+ }
+}
+
+class BufferIoExecutor : public ShaderExecutor
+{
+public:
+ BufferIoExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~BufferIoExecutor (void);
+
+ virtual void log (tcu::TestLog& dst) const { /* TODO */ (void)dst; }
+
+protected:
+ enum
+ {
+ INPUT_BUFFER_BINDING = 0,
+ OUTPUT_BUFFER_BINDING = 1,
+ };
+
+ void initBuffers (const Context& ctx, int numValues);
+ VkBuffer getInputBuffer (void) const { return *m_inputBuffer; }
+ VkBuffer getOutputBuffer (void) const { return *m_outputBuffer; }
+ deUint32 getInputStride (void) const { return getLayoutStride(m_inputLayout); }
+ deUint32 getOutputStride (void) const { return getLayoutStride(m_outputLayout); }
+
+ void uploadInputBuffer (const Context& ctx, const void* const* inputPtrs, int numValues);
+ void readOutputBuffer (const Context& ctx, void* const* outputPtrs, int numValues);
+
+ static void declareBufferBlocks (std::ostream& src, const ShaderSpec& spec);
+ static void generateExecBufferIo(std::ostream& src, const ShaderSpec& spec, const char* invocationNdxName);
+
+protected:
+ Move<VkBuffer> m_inputBuffer;
+ Move<VkBuffer> m_outputBuffer;
+
+private:
+ struct VarLayout
+ {
+ deUint32 offset;
+ deUint32 stride;
+ deUint32 matrixStride;
+
+ VarLayout (void) : offset(0), stride(0), matrixStride(0) {}
+ };
+
+ static void computeVarLayout (const std::vector<Symbol>& symbols, std::vector<VarLayout>* layout);
+ static deUint32 getLayoutStride (const vector<VarLayout>& layout);
+
+ static void copyToBuffer (const glu::VarType& varType, const VarLayout& layout, int numValues, const void* srcBasePtr, void* dstBasePtr);
+ static void copyFromBuffer (const glu::VarType& varType, const VarLayout& layout, int numValues, const void* srcBasePtr, void* dstBasePtr);
+
+ de::MovePtr<Allocation> m_inputAlloc;
+ de::MovePtr<Allocation> m_outputAlloc;
+
+ vector<VarLayout> m_inputLayout;
+ vector<VarLayout> m_outputLayout;
+};
+
+BufferIoExecutor::BufferIoExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : ShaderExecutor (shaderSpec, shaderType)
+{
+ computeVarLayout(m_shaderSpec.inputs, &m_inputLayout);
+ computeVarLayout(m_shaderSpec.outputs, &m_outputLayout);
+}
+
+BufferIoExecutor::~BufferIoExecutor (void)
+{
+}
+
+inline deUint32 BufferIoExecutor::getLayoutStride (const vector<VarLayout>& layout)
+{
+ return layout.empty() ? 0 : layout[0].stride;
+}
+
+void BufferIoExecutor::computeVarLayout (const std::vector<Symbol>& symbols, std::vector<VarLayout>* layout)
+{
+ deUint32 maxAlignment = 0;
+ deUint32 curOffset = 0;
+
+ DE_ASSERT(layout != DE_NULL);
+ DE_ASSERT(layout->empty());
+ layout->resize(symbols.size());
+
+ for (size_t varNdx = 0; varNdx < symbols.size(); varNdx++)
+ {
+ const Symbol& symbol = symbols[varNdx];
+ const glu::DataType basicType = symbol.varType.getBasicType();
+ VarLayout& layoutEntry = (*layout)[varNdx];
+
+ if (glu::isDataTypeScalarOrVector(basicType))
+ {
+ const deUint32 alignment = getVecStd430ByteAlignment(basicType);
+ const deUint32 size = (deUint32)glu::getDataTypeScalarSize(basicType) * (int)sizeof(deUint32);
+
+ curOffset = (deUint32)deAlign32((int)curOffset, (int)alignment);
+ maxAlignment = de::max(maxAlignment, alignment);
+
+ layoutEntry.offset = curOffset;
+ layoutEntry.matrixStride = 0;
+
+ curOffset += size;
+ }
+ else if (glu::isDataTypeMatrix(basicType))
+ {
+ const int numVecs = glu::getDataTypeMatrixNumColumns(basicType);
+ const glu::DataType vecType = glu::getDataTypeFloatVec(glu::getDataTypeMatrixNumRows(basicType));
+ const deUint32 vecAlignment = getVecStd430ByteAlignment(vecType);
+
+ curOffset = (deUint32)deAlign32((int)curOffset, (int)vecAlignment);
+ maxAlignment = de::max(maxAlignment, vecAlignment);
+
+ layoutEntry.offset = curOffset;
+ layoutEntry.matrixStride = vecAlignment;
+
+ curOffset += vecAlignment*numVecs;
+ }
+ else
+ DE_ASSERT(false);
+ }
+
+ {
+ const deUint32 totalSize = (deUint32)deAlign32(curOffset, maxAlignment);
+
+ for (vector<VarLayout>::iterator varIter = layout->begin(); varIter != layout->end(); ++varIter)
+ varIter->stride = totalSize;
+ }
+}
+
+void BufferIoExecutor::declareBufferBlocks (std::ostream& src, const ShaderSpec& spec)
+{
+ // Input struct
+ if (!spec.inputs.empty())
+ {
+ glu::StructType inputStruct("Inputs");
+ for (vector<Symbol>::const_iterator symIter = spec.inputs.begin(); symIter != spec.inputs.end(); ++symIter)
+ inputStruct.addMember(symIter->name.c_str(), symIter->varType);
+ src << glu::declare(&inputStruct) << ";\n";
+ }
+
+ // Output struct
+ {
+ glu::StructType outputStruct("Outputs");
+ for (vector<Symbol>::const_iterator symIter = spec.outputs.begin(); symIter != spec.outputs.end(); ++symIter)
+ outputStruct.addMember(symIter->name.c_str(), symIter->varType);
+ src << glu::declare(&outputStruct) << ";\n";
+ }
+
+ src << "\n";
+
+ if (!spec.inputs.empty())
+ {
+ src << "layout(set = 0, binding = " << int(INPUT_BUFFER_BINDING) << ", std140) buffer InBuffer\n"
+ << "{\n"
+ << " Inputs inputs[];\n"
+ << "};\n";
+ }
+
+ src << "layout(set = 0, binding = " << int(OUTPUT_BUFFER_BINDING) << ", std140) buffer OutBuffer\n"
+ << "{\n"
+ << " Outputs outputs[];\n"
+ << "};\n"
+ << "\n";
+}
+
+void BufferIoExecutor::generateExecBufferIo (std::ostream& src, const ShaderSpec& spec, const char* invocationNdxName)
+{
+ for (vector<Symbol>::const_iterator symIter = spec.inputs.begin(); symIter != spec.inputs.end(); ++symIter)
+ src << "\t" << glu::declare(symIter->varType, symIter->name) << " = inputs[" << invocationNdxName << "]." << symIter->name << ";\n";
+
+ for (vector<Symbol>::const_iterator symIter = spec.outputs.begin(); symIter != spec.outputs.end(); ++symIter)
+ src << "\t" << glu::declare(symIter->varType, symIter->name) << ";\n";
+
+ src << "\n";
+
+ {
+ std::istringstream opSrc (spec.source);
+ std::string line;
+
+ while (std::getline(opSrc, line))
+ src << "\t" << line << "\n";
+ }
+
+ src << "\n";
+ for (vector<Symbol>::const_iterator symIter = spec.outputs.begin(); symIter != spec.outputs.end(); ++symIter)
+ src << "\toutputs[" << invocationNdxName << "]." << symIter->name << " = " << symIter->name << ";\n";
+}
+
+void BufferIoExecutor::copyToBuffer (const glu::VarType& varType, const VarLayout& layout, int numValues, const void* srcBasePtr, void* dstBasePtr)
+{
+ if (varType.isBasicType())
+ {
+ const glu::DataType basicType = varType.getBasicType();
+ const bool isMatrix = glu::isDataTypeMatrix(basicType);
+ const int scalarSize = glu::getDataTypeScalarSize(basicType);
+ const int numVecs = isMatrix ? glu::getDataTypeMatrixNumColumns(basicType) : 1;
+ const int numComps = scalarSize / numVecs;
+
+ for (int elemNdx = 0; elemNdx < numValues; elemNdx++)
+ {
+ for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
+ {
+ const int srcOffset = (int)sizeof(deUint32) * (elemNdx * scalarSize + vecNdx * numComps);
+ const int dstOffset = layout.offset + layout.stride * elemNdx + (isMatrix ? layout.matrixStride * vecNdx : 0);
+ const deUint8* srcPtr = (const deUint8*)srcBasePtr + srcOffset;
+ deUint8* dstPtr = (deUint8*)dstBasePtr + dstOffset;
+
+ deMemcpy(dstPtr, srcPtr, sizeof(deUint32) * numComps);
+ }
+ }
+ }
+ else
+ throw tcu::InternalError("Unsupported type");
+}
+
+void BufferIoExecutor::copyFromBuffer (const glu::VarType& varType, const VarLayout& layout, int numValues, const void* srcBasePtr, void* dstBasePtr)
+{
+ if (varType.isBasicType())
+ {
+ const glu::DataType basicType = varType.getBasicType();
+ const bool isMatrix = glu::isDataTypeMatrix(basicType);
+ const int scalarSize = glu::getDataTypeScalarSize(basicType);
+ const int numVecs = isMatrix ? glu::getDataTypeMatrixNumColumns(basicType) : 1;
+ const int numComps = scalarSize / numVecs;
+
+ for (int elemNdx = 0; elemNdx < numValues; elemNdx++)
+ {
+ for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
+ {
+ const int srcOffset = layout.offset + layout.stride * elemNdx + (isMatrix ? layout.matrixStride * vecNdx : 0);
+ const int dstOffset = (int)sizeof(deUint32) * (elemNdx * scalarSize + vecNdx * numComps);
+ const deUint8* srcPtr = (const deUint8*)srcBasePtr + srcOffset;
+ deUint8* dstPtr = (deUint8*)dstBasePtr + dstOffset;
+
+ deMemcpy(dstPtr, srcPtr, sizeof(deUint32) * numComps);
+ }
+ }
+ }
+ else
+ throw tcu::InternalError("Unsupported type");
+}
+
+void BufferIoExecutor::uploadInputBuffer (const Context& ctx, const void* const* inputPtrs, int numValues)
+{
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+
+ const deUint32 inputStride = getLayoutStride(m_inputLayout);
+ const int inputBufferSize = inputStride * numValues;
+
+ if (inputBufferSize == 0)
+ return; // No inputs
+
+ DE_ASSERT(m_shaderSpec.inputs.size() == m_inputLayout.size());
+ for (size_t inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
+ {
+ const glu::VarType& varType = m_shaderSpec.inputs[inputNdx].varType;
+ const VarLayout& layout = m_inputLayout[inputNdx];
+
+ copyToBuffer(varType, layout, numValues, inputPtrs[inputNdx], m_inputAlloc->getHostPtr());
+ }
+
+ flushMappedMemoryRange(vk, vkDevice, m_inputAlloc->getMemory(), m_inputAlloc->getOffset(), inputBufferSize);
+}
+
+void BufferIoExecutor::readOutputBuffer (const Context& ctx, void* const* outputPtrs, int numValues)
+{
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+
+ const deUint32 outputStride = getLayoutStride(m_outputLayout);
+ const int outputBufferSize = numValues * outputStride;
+
+ DE_ASSERT(outputBufferSize > 0); // At least some outputs are required.
+
+ invalidateMappedMemoryRange(vk, vkDevice, m_outputAlloc->getMemory(), m_outputAlloc->getOffset(), outputBufferSize);
+
+ DE_ASSERT(m_shaderSpec.outputs.size() == m_outputLayout.size());
+ for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
+ {
+ const glu::VarType& varType = m_shaderSpec.outputs[outputNdx].varType;
+ const VarLayout& layout = m_outputLayout[outputNdx];
+
+ copyFromBuffer(varType, layout, numValues, m_outputAlloc->getHostPtr(), outputPtrs[outputNdx]);
+ }
+}
+
+void BufferIoExecutor::initBuffers (const Context& ctx, int numValues)
+{
+ const deUint32 inputStride = getLayoutStride(m_inputLayout);
+ const deUint32 outputStride = getLayoutStride(m_outputLayout);
+ const size_t inputBufferSize = numValues * inputStride;
+ const size_t outputBufferSize = numValues * outputStride;
+
+ // Upload data to buffer
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+ const deUint32 queueFamilyIndex = ctx.getUniversalQueueFamilyIndex();
+ Allocator& memAlloc = ctx.getDefaultAllocator();
+
+ const VkBufferCreateInfo inputBufferParams =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkBufferCreateFlags flags;
+ inputBufferSize, // VkDeviceSize size;
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
+ };
+
+ m_inputBuffer = createBuffer(vk, vkDevice, &inputBufferParams);
+ m_inputAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *m_inputBuffer), MemoryRequirement::HostVisible);
+
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *m_inputBuffer, m_inputAlloc->getMemory(), m_inputAlloc->getOffset()));
+
+ const VkBufferCreateInfo outputBufferParams =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkBufferCreateFlags flags;
+ outputBufferSize, // VkDeviceSize size;
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
+ };
+
+ m_outputBuffer = createBuffer(vk, vkDevice, &outputBufferParams);
+ m_outputAlloc = memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *m_outputBuffer), MemoryRequirement::HostVisible);
+
+ VK_CHECK(vk.bindBufferMemory(vkDevice, *m_outputBuffer, m_outputAlloc->getMemory(), m_outputAlloc->getOffset()));
+}
+
+// ComputeShaderExecutor
+
+class ComputeShaderExecutor : public BufferIoExecutor
+{
+public:
+ ComputeShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~ComputeShaderExecutor (void);
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+ virtual void execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs);
+
+protected:
+ static std::string generateComputeShader (const ShaderSpec& spec);
+
+ tcu::IVec3 m_maxWorkSize;
+};
+
+ComputeShaderExecutor::ComputeShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : BufferIoExecutor (shaderSpec, shaderType)
+{
+}
+
+ComputeShaderExecutor::~ComputeShaderExecutor (void)
+{
+}
+
+std::string ComputeShaderExecutor::generateComputeShader (const ShaderSpec& spec)
+{
+ std::ostringstream src;
+ src << "#version 310 es\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!spec.globalDeclarations.empty())
+ src << spec.globalDeclarations << "\n";
+
+ src << "layout(local_size_x = 1) in;\n"
+ << "\n";
+
+ declareBufferBlocks(src, spec);
+
+ src << "void main (void)\n"
+ << "{\n"
+ << " uint invocationNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z\n"
+ << " + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n";
+
+ generateExecBufferIo(src, spec, "invocationNdx");
+
+ src << "}\n";
+
+ return src.str();
+}
+
+void ComputeShaderExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("compute") << glu::ComputeSource(generateComputeShader(m_shaderSpec));
+}
+
+void ComputeShaderExecutor::execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs)
+{
+ checkSupported(ctx, m_shaderType);
+
+ const int maxValuesPerInvocation = m_maxWorkSize[0];
+
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+ const VkQueue queue = ctx.getUniversalQueue();
+ const deUint32 queueFamilyIndex = ctx.getUniversalQueueFamilyIndex();
+
+ Move<VkShaderModule> computeShaderModule;
+ Move<VkPipeline> computePipeline;
+ Move<VkPipelineLayout> pipelineLayout;
+ Move<VkCommandPool> cmdPool;
+ Move<VkDescriptorPool> descriptorPool;
+ Move<VkDescriptorSetLayout> descriptorSetLayout;
+ Move<VkDescriptorSet> descriptorSet;
+ Move<VkFence> fence;
+
+ initBuffers(ctx, numValues);
+
+ // Setup input buffer & copy data
+ uploadInputBuffer(ctx, inputs, numValues);
+
+ // Create command pool
+ {
+ const VkCommandPoolCreateInfo cmdPoolParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, // VkCmdPoolCreateFlags flags;
+ queueFamilyIndex // deUint32 queueFamilyIndex;
+ };
+
+ cmdPool = createCommandPool(vk, vkDevice, &cmdPoolParams);
+ }
+
+ // Create command buffer
+ const VkCommandBufferAllocateInfo cmdBufferParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *cmdPool, // VkCmdPool cmdPool;
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCmdBufferLevel level;
+ 1u // deUint32 bufferCount;
+ };
+
+ const VkCommandBufferBeginInfo cmdBufferBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkCmdBufferOptimizeFlags flags;
+ DE_NULL, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ DE_NULL, // VkFramebuffer framebuffer;
+ VK_FALSE, // VkBool32 occlusionQueryEnable;
+ (VkQueryControlFlags)0, // VkQueryControlFlags queryFlags;
+ (VkQueryPipelineStatisticFlags)0 // VkQueryPipelineStatisticFlags pipelineStatistics;
+
+ };
+
+ const VkDescriptorSetLayoutBinding layoutBindings[2] =
+ {
+ {
+ 0u, // deUint32 binding;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ 1u, // deUint32 descriptorCount;
+ VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags stageFlags;
+ DE_NULL // const VkSampler* pImmutableSamplers;
+ },
+ {
+ 0u, // deUint32 binding;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ 1u, // deUint32 descriptorCount;
+ VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags stageFlags;
+ DE_NULL // const VkSampler* pImmutableSamplers;
+ }
+ };
+
+ const VkDescriptorSetLayoutCreateInfo descriptorLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // cost void* pNexŧ;
+ (VkDescriptorSetLayoutCreateFlags)0, // VkDescriptorSetLayoutCreateFlags flags;
+ DE_LENGTH_OF_ARRAY(layoutBindings), // deUint32 count;
+ layoutBindings // const VkDescriptorSetLayoutBinding pBinding;
+ };
+
+ descriptorSetLayout = createDescriptorSetLayout(vk, vkDevice, &descriptorLayoutParams);
+
+ const VkDescriptorPoolSize descriptorPoolSizes[] =
+ {
+ {
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType type;
+ 1u // deUint32 count;
+ },
+ {
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType type;
+ 1u // deUint32 count;
+ }
+ };
+
+ const VkDescriptorPoolCreateInfo descriptorPoolParams =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // void* pNext;
+ VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // VkDescriptorPoolUsage poolUsage;
+ 1u, // deUint32 maxSets;
+ DE_LENGTH_OF_ARRAY(descriptorPoolSizes), // deUint32 count;
+ descriptorPoolSizes // const VkDescriptorPoolSize* pPoolSizes;
+ };
+
+ descriptorPool = createDescriptorPool(vk, vkDevice, &descriptorPoolParams);
+
+ const VkDescriptorSetAllocateInfo allocInfo =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ DE_NULL,
+ *descriptorPool,
+ 1u,
+ &*descriptorSetLayout
+ };
+
+ descriptorSet = allocateDescriptorSet(vk, vkDevice, &allocInfo);
+
+ // Create pipeline layout
+ {
+ const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ 0u, // deUint32 CdescriptorSetCount;
+ DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+
+ pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+ }
+
+ // Create shaders
+ {
+ computeShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("compute"), 0);
+ }
+
+ // create pipeline
+ {
+ const VkPipelineShaderStageCreateInfo shaderStageParams[1] =
+ {
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0u, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagsBit stage;
+ *computeShaderModule, // VkShaderModule shader;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ }
+ };
+
+ const VkComputePipelineCreateInfo computePipelineParams =
+ {
+ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
+ *shaderStageParams, // VkPipelineShaderStageCreateInfo cs;
+ *pipelineLayout, // VkPipelineLayout layout;
+ 0u, // VkPipeline basePipelineHandle;
+ 0u, // int32_t basePipelineIndex;
+ };
+
+ computePipeline = createComputePipeline(vk, vkDevice, DE_NULL, &computePipelineParams);
+ }
+
+ // Create fence
+ {
+ const VkFenceCreateInfo fenceParams =
+ {
+ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u // VkFenceCreateFlags flags;
+ };
+ fence = createFence(vk, vkDevice, &fenceParams);
+ }
+
+ int curOffset = 0;
+ const deUint32 inputStride = getInputStride();
+ const deUint32 outputStride = getOutputStride();
+
+ while (curOffset < numValues)
+ {
+ Move<VkCommandBuffer> cmdBuffer;
+ const int numToExec = de::min(maxValuesPerInvocation, numValues-curOffset);
+
+ const VkDescriptorBufferInfo descriptorBufferInfo[] =
+ {
+ {
+ *m_inputBuffer, // VkBuffer buffer;
+ curOffset * inputStride, // VkDeviceSize offset;
+ numToExec * inputStride // VkDeviceSize range;
+ },
+ {
+ *m_outputBuffer, // VkBuffer buffer;
+ curOffset * outputStride, // VkDeviceSize offset;
+ numToExec * outputStride // VkDeviceSize range;
+ }
+ };
+
+ const VkWriteDescriptorSet writeDescritporSets[] =
+ {
+ {
+ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *descriptorSet, // VkDescriptorSet destSet;
+ 0u, // deUint32 destBinding;
+ 0u, // deUint32 destArrayElement;
+ 2u, // deUint32 count;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ DE_NULL, // const VkDescriptorImageInfo* pImageInfo;
+ descriptorBufferInfo, // const VkDescriptorBufferInfo* pBufferInfo;
+ DE_NULL // const VkBufferView* pTexelBufferView;
+ }
+ };
+
+ vk.updateDescriptorSets(vkDevice, 1, writeDescritporSets, 0u, DE_NULL);
+
+ cmdBuffer = allocateCommandBuffer(vk, vkDevice, &cmdBufferParams);
+ VK_CHECK(vk.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
+
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
+
+ vk.cmdDispatch(*cmdBuffer, numToExec, 1, 1);
+
+ VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
+
+ curOffset += numToExec;
+
+ // Execute
+ {
+ VK_CHECK(vk.resetFences(vkDevice, 1, &fence.get()));
+
+ const VkSubmitInfo submitInfo =
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ DE_NULL,
+ 0u,
+ (const VkSemaphore*)DE_NULL,
+ 1u,
+ &cmdBuffer.get(),
+ 0u,
+ (const VkSemaphore*)DE_NULL,
+ };
+
+ VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
+ VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), true, ~(0ull) /* infinity*/));
+ }
+ }
+
+ // Read back data
+ readOutputBuffer(ctx, outputs, numValues);
+}
+
+// Tessellation utils
+
+static std::string generateVertexShaderForTess (void)
+{
+ std::ostringstream src;
+ src << "#version 310 es\n"
+
+ << "void main (void)\n{\n"
+ << " gl_Position = vec4(gl_VertexID/2, gl_VertexID%2, 0.0, 1.0);\n"
+ << "}\n";
+
+ return src.str();
+}
+
+class TessellationExecutor : public BufferIoExecutor
+{
+public:
+ TessellationExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~TessellationExecutor (void);
+
+ void renderTess (const Context& ctx, deUint32 vertexCount);
+};
+
+TessellationExecutor::TessellationExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : BufferIoExecutor (shaderSpec, shaderType)
+{
+}
+
+TessellationExecutor::~TessellationExecutor (void)
+{
+}
+
+void TessellationExecutor::renderTess (const Context& ctx, deUint32 vertexCount)
+{
+ const VkDevice vkDevice = ctx.getDevice();
+ const DeviceInterface& vk = ctx.getDeviceInterface();
+ const VkQueue queue = ctx.getUniversalQueue();
+ const deUint32 queueFamilyIndex = ctx.getUniversalQueueFamilyIndex();
+ Allocator& memAlloc = ctx.getDefaultAllocator();
+
+ const tcu::IVec2 renderSize (DEFAULT_RENDER_WIDTH, DEFAULT_RENDER_HEIGHT);
+
+ Move<VkImage> colorImage;
+ de::MovePtr<Allocation> colorImageAlloc;
+ VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ Move<VkImageView> colorImageView;
+
+ Move<VkRenderPass> renderPass;
+ Move<VkFramebuffer> framebuffer;
+ Move<VkPipelineLayout> pipelineLayout;
+ Move<VkPipeline> graphicsPipeline;
+
+ Move<VkShaderModule> vertexShaderModule;
+ Move<VkShaderModule> tessControlShaderModule;
+ Move<VkShaderModule> tessEvalShaderModule;
+ Move<VkShaderModule> fragmentShaderModule;
+
+ Move<VkCommandPool> cmdPool;
+ Move<VkCommandBuffer> cmdBuffer;
+
+ Move<VkFence> fence;
+
+ Move<VkDescriptorPool> descriptorPool;
+ Move<VkDescriptorSetLayout> descriptorSetLayout;
+ Move<VkDescriptorSet> descriptorSet;
+
+ // Create color image
+ {
+ const VkImageCreateInfo colorImageParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ colorFormat, // VkFormat format;
+ { renderSize.x(), renderSize.y(), 1u }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arraySize;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 1u, // deUint32 queueFamilyCount;
+ &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
+ };
+
+ colorImage = createImage(vk, vkDevice, &colorImageParams);
+
+ // Allocate and bind color image memory
+ colorImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *colorImage), MemoryRequirement::Any);
+ VK_CHECK(vk.bindImageMemory(vkDevice, *colorImage, colorImageAlloc->getMemory(), colorImageAlloc->getOffset()));
+ }
+
+ // Create color attachment view
+ {
+ const VkImageViewCreateInfo colorImageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkImageViewCreateFlags flags;
+ *colorImage, // VkImage image;
+ VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
+ colorFormat, // VkFormat format;
+ {
+ VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle r;
+ VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle g;
+ VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle b;
+ VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle a;
+ }, // VkComponentsMapping components;
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
+ 0u, // deUint32 baseMipLevel;
+ 1u, // deUint32 mipLevels;
+ 0u, // deUint32 baseArraylayer;
+ 1u // deUint32 layerCount;
+ } // VkImageSubresourceRange subresourceRange;
+ };
+
+ colorImageView = createImageView(vk, vkDevice, &colorImageViewParams);
+ }
+
+ // Create render pass
+ {
+ const VkAttachmentDescription colorAttachmentDescription =
+ {
+ 0u, // VkAttachmentDescriptorFlags flags;
+ colorFormat, // VkFormat format;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
+ };
+
+ const VkAttachmentDescription attachments[1] =
+ {
+ colorAttachmentDescription
+ };
+
+ const VkAttachmentReference colorAttachmentReference =
+ {
+ 0u, // deUint32 attachment;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
+ };
+
+ const VkSubpassDescription subpassDescription =
+ {
+ 0u, // VkSubpassDescriptionFlags flags;
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // deUint32 inputCount;
+ DE_NULL, // const VkAttachmentReference* pInputAttachments;
+ 1u, // deUint32 colorCount;
+ &colorAttachmentReference, // const VkAttachmentReference* pColorAttachments;
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments;
+ DE_NULL, // VkAttachmentReference depthStencilAttachment;
+ 0u, // deUint32 preserveCount;
+ DE_NULL // const VkAttachmentReference* pPreserveAttachments;
+ };
+
+
+ const VkRenderPassCreateInfo renderPassParams =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkRenderPassCreateFlags flags;
+ 1u, // deUint32 attachmentCount;
+ attachments, // const VkAttachmentDescription* pAttachments;
+ 1u, // deUint32 subpassCount;
+ &subpassDescription, // const VkSubpassDescription* pSubpasses;
+ 0u, // deUint32 dependencyCount;
+ DE_NULL // const VkSubpassDependency* pDependencies;
+ };
+
+ renderPass = createRenderPass(vk, vkDevice, &renderPassParams);
+ }
+
+ // Create framebuffer
+ {
+ const VkFramebufferCreateInfo framebufferParams =
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkFramebufferCreateFlags flags;
+ *renderPass, // VkRenderPass renderPass;
+ 1u, // deUint32 attachmentCount;
+ &*colorImageView, // const VkAttachmentBindInfo* pAttachments;
+ (deUint32)renderSize.x(), // deUint32 width;
+ (deUint32)renderSize.y(), // deUint32 height;
+ 1u // deUint32 layers;
+ };
+
+ framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
+ }
+
+ // Create descriptors
+ {
+ const VkDescriptorSetLayoutBinding layoutBindings[2] =
+ {
+ {
+ 0u, // deUint32 binding;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ 1u, // deUint32 arraySize;
+ VK_SHADER_STAGE_ALL, // VkShaderStageFlags stageFlags;
+ DE_NULL // const VkSampler* pImmutableSamplers;
+ },
+ {
+ 0u, // deUint32 binding;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ 1u, // deUint32 arraySize;
+ VK_SHADER_STAGE_ALL, // VkShaderStageFlags stageFlags;
+ DE_NULL // const VkSampler* pImmutableSamplers;
+ }
+ };
+
+ const VkDescriptorSetLayoutCreateInfo descriptorLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // cost void* pNexŧ;
+ (VkDescriptorSetLayoutCreateFlags)0, // VkDescriptorSetLayoutCreateFlags flags;
+ DE_LENGTH_OF_ARRAY(layoutBindings), // deUint32 count;
+ layoutBindings // const VkDescriptorSetLayoutBinding pBinding;
+ };
+
+ descriptorSetLayout = createDescriptorSetLayout(vk, vkDevice, &descriptorLayoutParams);
+
+ const VkDescriptorPoolSize descriptorPoolSizes[] =
+ {
+ {
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType type;
+ 1u // deUint32 count;
+ },
+ {
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType type;
+ 1u // deUint32 count;
+ }
+ };
+
+ const VkDescriptorPoolCreateInfo descriptorPoolParams =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // void* pNext;
+ VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, //VkDescriptorPoolUsage poolUsage;
+ 1u, //deUint32 maxSets;
+ DE_LENGTH_OF_ARRAY(descriptorPoolSizes), // deUint32 count;
+ descriptorPoolSizes // const VkDescriptorPoolSize* pTypeCount
+ };
+
+ descriptorPool = createDescriptorPool(vk, vkDevice, &descriptorPoolParams);
+
+ const VkDescriptorSetAllocateInfo allocInfo =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ DE_NULL,
+ *descriptorPool,
+ 1u,
+ &*descriptorSetLayout
+ };
+
+ descriptorSet = allocateDescriptorSet(vk, vkDevice, &allocInfo);
+
+ const VkDescriptorBufferInfo descriptorBufferInfo[] =
+ {
+ {
+ *m_inputBuffer, // VkBuffer buffer;
+ 0u, // VkDeviceSize offset;
+ VK_WHOLE_SIZE // VkDeviceSize range;
+ },
+ {
+ *m_outputBuffer, // VkBuffer buffer;
+ 0u, // VkDeviceSize offset;
+ VK_WHOLE_SIZE // VkDeviceSize range;
+ },
+ };
+
+ const VkWriteDescriptorSet writeDescritporSets[] =
+ {
+ {
+ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *descriptorSet, // VkDescriptorSet destSet;
+ 0u, // deUint32 destBinding;
+ 0u, // deUint32 destArrayElement;
+ 2u, // deUint32 count;
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // VkDescriptorType descriptorType;
+ DE_NULL, // const VkDescriptorImageInfo* pImageInfo;
+ descriptorBufferInfo, // const VkDescriptorBufferInfo* pBufferInfo;
+ DE_NULL // const VkBufferView* pTexelBufferView;
+ }
+ };
+
+ vk.updateDescriptorSets(vkDevice, 1, writeDescritporSets, 0u, DE_NULL);
+ }
+
+ // Create pipeline layout
+ {
+ const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ 1u, // deUint32 descriptorSetCount;
+ &*descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL // const VkPushConstantRange* pPushConstantRanges;
+ };
+
+ pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+ }
+
+ // Create shader modules
+ {
+ vertexShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("vert"), 0);
+ tessControlShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("tess_control"), 0);
+ tessEvalShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("tess_eval"), 0);
+ fragmentShaderModule = createShaderModule(vk, vkDevice, ctx.getBinaryCollection().get("frag"), 0);
+ }
+
+ // Create pipeline
+ {
+ const VkPipelineShaderStageCreateInfo shaderStageParams[4] =
+ {
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBit stage;
+ *vertexShaderModule, // VkShaderModule shader;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, // VkShaderStageFlagBit stage;
+ *tessControlShaderModule, // VkShaderModule shader;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, // VkShaderStageFlagBit stage;
+ *tessEvalShaderModule, // VkShaderModule shader;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBit stage;
+ *fragmentShaderModule, // VkShaderModule shader;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ }
+ };
+
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
+ 0u, // deUint32 bindingCount;
+ DE_NULL, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ 0u, // deUint32 attributeCount;
+ DE_NULL, // const VkVertexInputAttributeDescription* pvertexAttributeDescriptions;
+ };
+
+ const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, // VkPrimitiveTopology topology;
+ DE_FALSE // VkBool32 primitiveRestartEnable;
+ };
+
+ struct VkPipelineTessellationStateCreateInfo tessellationStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineTesselationStateCreateFlags)0, // VkPipelineTessellationStateCreateFlags flags;
+ vertexCount // uint32_t patchControlPoints;
+ };
+
+ const VkViewport viewport =
+ {
+ 0.0f, // float originX;
+ 0.0f, // float originY;
+ (float)renderSize.x(), // float width;
+ (float)renderSize.y(), // float height;
+ 0.0f, // float minDepth;
+ 1.0f // float maxDepth;
+ };
+
+ const VkRect2D scissor =
+ {
+ {
+ 0u, // deUint32 x;
+ 0u, // deUint32 y;
+ }, // VkOffset2D offset;
+ {
+ renderSize.x(), // deUint32 width;
+ renderSize.y(), // deUint32 height;
+ }, // VkExtent2D extent;
+ };
+
+ const VkPipelineViewportStateCreateInfo viewportStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineViewportStateCreateFlags)0, // VkPipelineViewPortStateCreateFlags flags;
+ 1u, // deUint32 viewportCount;
+ &viewport, // const VkViewport* pViewports;
+ 1u, // deUint32 scissorsCount;
+ &scissor // const VkRect2D* pScissors;
+ };
+
+ const VkPipelineRasterizationStateCreateInfo rasterStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineRasterizationStateCreateFlags)0, // VkPipelineRasterizationStageCreateFlags flags;
+ VK_FALSE, // VkBool32 depthClipEnable;
+ VK_FALSE, // VkBool32 rasterizerDiscardEnable;
+ VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
+ VK_CULL_MODE_NONE, // VkCullMode cullMode;
+ VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
+ VK_FALSE, // VkBool32 depthBiasEnable;
+ 0.0f, // float depthBias;
+ 0.0f, // float depthBiasClamp;
+ 0.0f, // float slopeScaledDepthBias;
+ 1.0f // float lineWidth;
+ };
+
+ const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
+ {
+ VK_FALSE, // VkBool32 blendEnable;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor srcBlendColor;
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor destBlendColor;
+ VK_BLEND_OP_ADD, // VkBlendOp blendOpColor;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor srcBlendAlpha;
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor destBlendAlpha;
+ VK_BLEND_OP_ADD, // VkBlendOp blendOpAlpha;
+ (VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT |
+ VK_COLOR_COMPONENT_A_BIT) // VkColorComponentFlags colorWriteMask;
+ };
+
+ const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags
+ VK_FALSE, // VkBool32 logicOpEnable;
+ VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
+ 1u, // deUint32 attachmentCount;
+ &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
+ { 0.0f, 0.0f, 0.0f, 0.0f } // float blendConst[4];
+ };
+
+ const VkPipelineDynamicStateCreateInfo dynamicStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineDynamicStateCreateFlags)0, // VkPipelineDynamicStateCreateFlags flags;
+ 0u, // deUint32 dynamicStateCount;
+ DE_NULL // const VkDynamicState* pDynamicStates;
+ };
+
+ const VkGraphicsPipelineCreateInfo graphicsPipelineParams =
+ {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineCreateFlags flags;
+ 4u, // deUint32 stageCount;
+ shaderStageParams, // const VkPipelineShaderStageCreateInfo* pStages;
+ &vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
+ &inputAssemblyStateParams, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
+ &tessellationStateParams, // const VkPipelineTessellationStateCreateInfo* pTessellationState;
+ &viewportStateParams, // const VkPipelineViewportStateCreateInfo* pViewportState;
+ &rasterStateParams, // const VkPipelineRasterStateCreateInfo* pRasterState;
+ DE_NULL, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
+ DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
+ &colorBlendStateParams, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
+ &dynamicStateInfo, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
+ *pipelineLayout, // VkPipelineLayout layout;
+ *renderPass, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ 0u, // VkPipeline basePipelineHandle;
+ 0u // deInt32 basePipelineIndex;
+ };
+
+ graphicsPipeline = createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams);
+ }
+
+ // Create command pool
+ {
+ const VkCommandPoolCreateInfo cmdPoolParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, // VkCmdPoolCreateFlags flags;
+ queueFamilyIndex, // deUint32 queueFamilyIndex;
+ };
+
+ cmdPool = createCommandPool(vk, vkDevice, &cmdPoolParams);
+ }
+
+ // Create command buffer
+ {
+ const VkCommandBufferAllocateInfo cmdBufferParams =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *cmdPool, // VkCmdPool cmdPool;
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCmdBufferLevel level;
+ 0u // VkCmdBufferCreateFlags flags;
+ };
+
+ const VkCommandBufferBeginInfo cmdBufferBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkCmdBufferOptimizeFlags flags;
+ DE_NULL, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ DE_NULL, // VkFramebuffer framebuffer;
+ VK_FALSE, //VkBool32 occlusionQueryEnable;
+ (VkQueryControlFlags)0u, // VkQueryControlFlags queryFlags;
+ (VkQueryPipelineStatisticFlags)0u // VkQueryPipelineStatisticFlags pipelineStatistics;
+
+ };
+
+ const VkClearValue clearValues[1] =
+ {
+ getDefaultClearColor()
+ };
+
+ const VkRenderPassBeginInfo renderPassBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ *renderPass, // VkRenderPass renderPass;
+ *framebuffer, // VkFramebuffer framebuffer;
+ { { 0, 0 }, { renderSize.x(), renderSize.y() } }, // VkRect2D renderArea;
+ 1, // deUint32 attachmentCount;
+ clearValues // const VkClearValue* pClearValues;
+ };
+
+ cmdBuffer = allocateCommandBuffer(vk, vkDevice, &cmdBufferParams);
+
+ VK_CHECK(vk.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
+
+ vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
+
+ vk.cmdDraw(*cmdBuffer, vertexCount, 1, 0, 0);
+
+ vk.cmdEndRenderPass(*cmdBuffer);
+ VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
+ }
+
+ // Create fence
+ {
+ const VkFenceCreateInfo fenceParams =
+ {
+ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u // VkFenceCreateFlags flags;
+ };
+ fence = createFence(vk, vkDevice, &fenceParams);
+ }
+
+ // Execute Draw
+ {
+ VK_CHECK(vk.resetFences(vkDevice, 1, &fence.get()));
+ const VkSubmitInfo submitInfo =
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ DE_NULL,
+ 0u,
+ (const VkSemaphore*)0,
+ 1u,
+ &cmdBuffer.get(),
+ 0u,
+ (const VkSemaphore*)0,
+ };
+ VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
+ VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), true, ~(0ull) /* infinity*/));
+ }
+}
+
+// TessControlExecutor
+
+class TessControlExecutor : public TessellationExecutor
+{
+public:
+ TessControlExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~TessControlExecutor (void);
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+ virtual void execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs);
+
+protected:
+ static std::string generateTessControlShader (const ShaderSpec& shaderSpec);
+};
+
+TessControlExecutor::TessControlExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : TessellationExecutor (shaderSpec, shaderType)
+{
+}
+
+TessControlExecutor::~TessControlExecutor (void)
+{
+}
+
+std::string TessControlExecutor::generateTessControlShader (const ShaderSpec& shaderSpec)
+{
+ std::ostringstream src;
+ src << "#version 310 es\n"
+ "#extension GL_EXT_tessellation_shader : require\n\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ src << "\nlayout(vertices = 1) out;\n\n";
+
+ declareBufferBlocks(src, shaderSpec);
+
+ src << "void main (void)\n{\n";
+
+ for (int ndx = 0; ndx < 2; ndx++)
+ src << "\tgl_TessLevelInner[" << ndx << "] = 1.0;\n";
+
+ for (int ndx = 0; ndx < 4; ndx++)
+ src << "\tgl_TessLevelOuter[" << ndx << "] = 1.0;\n";
+
+ src << "\n"
+ << "\thighp uint invocationId = uint(gl_PrimitiveID);\n";
+
+ generateExecBufferIo(src, shaderSpec, "invocationId");
+
+ src << "}\n";
+
+ return src.str();
+}
+
+static std::string generateEmptyTessEvalShader ()
+{
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_EXT_tessellation_shader : require\n\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ src << "layout(triangles, ccw) in;\n";
+
+ src << "\nvoid main (void)\n{\n"
+ << "\tgl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
+ << "}\n";
+
+ return src.str();
+}
+
+void TessControlExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("vert") << glu::VertexSource(generateVertexShaderForTess());
+ programCollection.glslSources.add("tess_control") << glu::TessellationControlSource(generateTessControlShader(m_shaderSpec));
+ programCollection.glslSources.add("tess_eval") << glu::TessellationEvaluationSource(generateEmptyTessEvalShader());
+ programCollection.glslSources.add("frag") << glu::FragmentSource(generateEmptyFragmentSource());
+}
+
+void TessControlExecutor::execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs)
+{
+ checkSupported(ctx, m_shaderType);
+
+ initBuffers(ctx, numValues);
+
+ // Setup input buffer & copy data
+ uploadInputBuffer(ctx, inputs, numValues);
+
+ renderTess(ctx, 3 * numValues);
+
+ // Read back data
+ readOutputBuffer(ctx, outputs, numValues);
+}
+
+// TessEvaluationExecutor
+
+class TessEvaluationExecutor : public TessellationExecutor
+{
+public:
+ TessEvaluationExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+ virtual ~TessEvaluationExecutor (void);
+
+ virtual void setShaderSources (SourceCollections& programCollection) const;
+
+ virtual void execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs);
+
+protected:
+ static std::string generateTessEvalShader (const ShaderSpec& shaderSpec);
+};
+
+TessEvaluationExecutor::TessEvaluationExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : TessellationExecutor (shaderSpec, shaderType)
+{
+}
+
+TessEvaluationExecutor::~TessEvaluationExecutor (void)
+{
+}
+
+static std::string generatePassthroughTessControlShader (void)
+{
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_EXT_tessellation_shader : require\n\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ src << "layout(vertices = 1) out;\n\n";
+
+ src << "void main (void)\n{\n";
+
+ for (int ndx = 0; ndx < 2; ndx++)
+ src << "\tgl_TessLevelInner[" << ndx << "] = 1.0;\n";
+
+ for (int ndx = 0; ndx < 4; ndx++)
+ src << "\tgl_TessLevelOuter[" << ndx << "] = 1.0;\n";
+
+ src << "}\n";
+
+ return src.str();
+}
+
+std::string TessEvaluationExecutor::generateTessEvalShader (const ShaderSpec& shaderSpec)
+{
+ std::ostringstream src;
+
+ src << "#version 310 es\n"
+ "#extension GL_EXT_tessellation_shader : require\n\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n";
+
+ if (!shaderSpec.globalDeclarations.empty())
+ src << shaderSpec.globalDeclarations << "\n";
+
+ src << "\n";
+
+ src << "layout(isolines, equal_spacing) in;\n\n";
+
+ declareBufferBlocks(src, shaderSpec);
+
+ src << "void main (void)\n{\n"
+ << "\tgl_Position = vec4(gl_TessCoord.x, 0.0, 0.0, 1.0);\n"
+ << "\thighp uint invocationId = uint(gl_PrimitiveID) + (gl_TessCoord.x > 0.5 ? 1u : 0u);\n";
+
+ generateExecBufferIo(src, shaderSpec, "invocationId");
+
+ src << "}\n";
+
+ return src.str();
+}
+
+void TessEvaluationExecutor::setShaderSources (SourceCollections& programCollection) const
+{
+ programCollection.glslSources.add("vert") << glu::VertexSource(generateVertexShaderForTess());
+ programCollection.glslSources.add("tess_control") << glu::TessellationControlSource(generatePassthroughTessControlShader());
+ programCollection.glslSources.add("tess_eval") << glu::TessellationEvaluationSource(generateTessEvalShader(m_shaderSpec));
+ programCollection.glslSources.add("frag") << glu::FragmentSource(generateEmptyFragmentSource());
+}
+
+void TessEvaluationExecutor::execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs)
+{
+ checkSupported(ctx, m_shaderType);
+
+ const int alignedValues = deAlign32(numValues, 2);
+
+ // Initialize buffers with aligned value count to make room for padding
+ initBuffers(ctx, alignedValues);
+
+ // Setup input buffer & copy data
+ uploadInputBuffer(ctx, inputs, numValues);
+
+ renderTess(ctx, 2 * numValues);
+
+ // Read back data
+ readOutputBuffer(ctx, outputs, numValues);
+}
+
+} // anonymous
+
+// ShaderExecutor
+
+ShaderExecutor::ShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType)
+ : m_shaderSpec (shaderSpec)
+ , m_shaderType (shaderType)
+{
+}
+
+ShaderExecutor::~ShaderExecutor (void)
+{
+}
+
+// Utilities
+
+ShaderExecutor* createExecutor (glu::ShaderType shaderType, const ShaderSpec& shaderSpec)
+{
+ switch (shaderType)
+ {
+ case glu::SHADERTYPE_VERTEX: return new VertexShaderExecutor (shaderSpec, shaderType);
+ case glu::SHADERTYPE_TESSELLATION_CONTROL: return new TessControlExecutor (shaderSpec, shaderType);
+ case glu::SHADERTYPE_TESSELLATION_EVALUATION: return new TessEvaluationExecutor (shaderSpec, shaderType);
+ case glu::SHADERTYPE_GEOMETRY: return new GeometryShaderExecutor (shaderSpec, shaderType);
+ case glu::SHADERTYPE_FRAGMENT: return new FragmentShaderExecutor (shaderSpec, shaderType);
+ case glu::SHADERTYPE_COMPUTE: return new ComputeShaderExecutor (shaderSpec, shaderType);
+ default:
+ throw tcu::InternalError("Unsupported shader type");
+ }
+}
+
+} // shaderexecutor
+} // vkt
--- /dev/null
+#ifndef _VKTSHADEREXECUTOR_HPP
+#define _VKTSHADEREXECUTOR_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Vulkan ShaderExecutor
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTestCase.hpp"
+#include "vkPrograms.hpp"
+
+#include "gluVarType.hpp"
+
+#include <vector>
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+//! Shader input / output variable declaration.
+struct Symbol
+{
+ std::string name; //!< Symbol name.
+ glu::VarType varType; //!< Symbol type.
+
+
+ Symbol (void) {}
+ Symbol (const std::string& name_, const glu::VarType& varType_) : name(name_), varType(varType_) {}
+};
+
+//! Complete shader specification.
+struct ShaderSpec
+{
+ std::vector<Symbol> inputs;
+ std::vector<Symbol> outputs;
+ std::string globalDeclarations; //!< These are placed into global scope. Can contain uniform declarations for example.
+ std::string source; //!< Source snippet to be executed.
+
+ ShaderSpec (void) {}
+};
+
+//! Base class for shader executor.
+class ShaderExecutor
+{
+public:
+ virtual ~ShaderExecutor (void);
+
+ //! Log executor details (program etc.).
+ virtual void log (tcu::TestLog& log) const = 0;
+
+ //! Execute
+ virtual void execute (const Context& ctx, int numValues, const void* const* inputs, void* const* outputs) = 0;
+
+ virtual void setShaderSources (vk::SourceCollections& programCollection) const = 0;
+
+protected:
+ ShaderExecutor (const ShaderSpec& shaderSpec, glu::ShaderType shaderType);
+
+ const ShaderSpec m_shaderSpec;
+ const glu::ShaderType m_shaderType;
+};
+
+inline tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderExecutor* executor) { executor->log(log); return log; }
+inline tcu::TestLog& operator<< (tcu::TestLog& log, const ShaderExecutor& executor) { executor.log(log); return log; }
+
+ShaderExecutor* createExecutor(glu::ShaderType shaderType, const ShaderSpec& shaderSpec);
+
+} // shaderexecutor
+} // vkt
+
+#endif // _VKTSHADEREXECUTOR_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Vulkan shader render test cases
+ *//*--------------------------------------------------------------------*/
+
+#include "vktShaderExecutorTests.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include "vktShaderCommonFunctionTests.hpp"
+#include "vktShaderIntegerFunctionTests.hpp"
+#include "vktShaderPackingFunctionTests.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> shaderExecutorTests (new tcu::TestCaseGroup(testCtx, "ShaderExecutor", "shaderExecutor Tests"));
+
+ shaderExecutorTests->addChild(new ShaderCommonFunctionTests(testCtx));
+ shaderExecutorTests->addChild(new ShaderIntegerFunctionTests(testCtx));
+ shaderExecutorTests->addChild(new ShaderPackingFunctionTests(testCtx));
+
+ return shaderExecutorTests.release();
+}
+
+} // shaderexecutor
+} // vkt
--- /dev/null
+#ifndef _VKTSHADEREXECUTORTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Vulkan shader render test cases
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx);
+
+} // shaderexecutor
+} // vkt
+
+#endif // _VKTSHADEREXECUTORTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Integer built-in function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "vktShaderIntegerFunctionTests.hpp"
+#include "vktShaderExecutor.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuFloat.hpp"
+#include "deRandom.hpp"
+#include "deMath.h"
+#include "deString.h"
+#include "deInt32.h"
+#include "deSharedPtr.hpp"
+
+#include <iostream>
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+using std::vector;
+using std::string;
+using tcu::TestLog;
+
+using tcu::IVec2;
+using tcu::IVec3;
+using tcu::IVec4;
+using tcu::UVec2;
+using tcu::UVec3;
+using tcu::UVec4;
+
+// Utilities
+
+namespace
+{
+
+struct HexFloat
+{
+ const float value;
+ HexFloat (const float value_) : value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const HexFloat& v)
+{
+ return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
+}
+
+struct VarValue
+{
+ const glu::VarType& type;
+ const void* value;
+
+ VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
+{
+ DE_ASSERT(varValue.type.isBasicType());
+
+ const glu::DataType basicType = varValue.type.getBasicType();
+ const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
+ const int numComponents = glu::getDataTypeScalarSize(basicType);
+
+ if (numComponents > 1)
+ str << glu::getDataTypeName(basicType) << "(";
+
+ for (int compNdx = 0; compNdx < numComponents; compNdx++)
+ {
+ if (compNdx != 0)
+ str << ", ";
+
+ switch (scalarType)
+ {
+ case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
+ case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
+ case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
+ case glu::TYPE_BOOL: str << (((const deUint32*)varValue.value)[compNdx] != 0 ? "true" : "false"); break;
+
+ default:
+ DE_ASSERT(false);
+ }
+ }
+
+ if (numComponents > 1)
+ str << ")";
+
+ return str;
+}
+
+inline int getShaderUintBitCount (glu::ShaderType shaderType, glu::Precision precision)
+{
+ // \todo [2013-10-31 pyry] Query from GL for vertex and fragment shaders.
+ DE_UNREF(shaderType);
+ const int bitCounts[] = { 9, 16, 32 };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bitCounts) == glu::PRECISION_LAST);
+ return bitCounts[precision];
+}
+
+static inline deUint32 extendSignTo32 (deUint32 integer, deUint32 integerLength)
+{
+ DE_ASSERT(integerLength > 0 && integerLength <= 32);
+
+ return deUint32(0 - deInt32((integer & (1 << (integerLength - 1))) << 1)) | integer;
+}
+
+static inline deUint32 getLowBitMask (int integerLength)
+{
+ DE_ASSERT(integerLength >= 0 && integerLength <= 32);
+
+ // \note: shifting more or equal to 32 => undefined behavior. Avoid it by shifting in two parts (1 << (num-1) << 1)
+ if (integerLength == 0u)
+ return 0u;
+ return ((1u << ((deUint32)integerLength - 1u)) << 1u) - 1u;
+}
+
+static void generateRandomInputData (de::Random& rnd, glu::ShaderType shaderType, glu::DataType dataType, glu::Precision precision, deUint32* dst, int numValues)
+{
+ const int scalarSize = glu::getDataTypeScalarSize(dataType);
+ const deUint32 integerLength = (deUint32)getShaderUintBitCount(shaderType, precision);
+ const deUint32 integerMask = getLowBitMask(integerLength);
+ const bool isUnsigned = glu::isDataTypeUintOrUVec(dataType);
+
+ if (isUnsigned)
+ {
+ for (int valueNdx = 0; valueNdx < numValues; ++valueNdx)
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ dst[valueNdx*scalarSize + compNdx] = rnd.getUint32() & integerMask;
+ }
+ else
+ {
+ for (int valueNdx = 0; valueNdx < numValues; ++valueNdx)
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ dst[valueNdx*scalarSize + compNdx] = extendSignTo32(rnd.getUint32() & integerMask, integerLength);
+ }
+}
+
+static vector<int> getScalarSizes (const vector<Symbol>& symbols)
+{
+ vector<int> sizes(symbols.size());
+ for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
+ sizes[ndx] = symbols[ndx].varType.getScalarSize();
+ return sizes;
+}
+
+static int computeTotalScalarSize (const vector<Symbol>& symbols)
+{
+ int totalSize = 0;
+ for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
+ totalSize += sym->varType.getScalarSize();
+ return totalSize;
+}
+
+static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
+{
+ vector<void*> pointers (symbols.size());
+ int curScalarOffset = 0;
+
+ for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
+ {
+ const Symbol& var = symbols[varNdx];
+ const int scalarSize = var.varType.getScalarSize();
+
+ // Uses planar layout as input/output specs do not support strides.
+ pointers[varNdx] = &data[curScalarOffset];
+ curScalarOffset += scalarSize*numValues;
+ }
+
+ DE_ASSERT(curScalarOffset == (int)data.size());
+
+ return pointers;
+}
+
+static const char* getPrecisionPostfix (glu::Precision precision)
+{
+ static const char* s_postfix[] =
+ {
+ "_lowp",
+ "_mediump",
+ "_highp"
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[precision];
+}
+
+static const char* getShaderTypePostfix (glu::ShaderType shaderType)
+{
+ static const char* s_postfix[] =
+ {
+ "_vertex",
+ "_fragment",
+ "_geometry",
+ "_tess_control",
+ "_tess_eval",
+ "_compute"
+ };
+ DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[shaderType];
+}
+
+static std::string getIntegerFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+{
+ return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
+}
+
+static inline deUint32 reverseBits (deUint32 v)
+{
+ v = (((v & 0xaaaaaaaa) >> 1) | ((v & 0x55555555) << 1));
+ v = (((v & 0xcccccccc) >> 2) | ((v & 0x33333333) << 2));
+ v = (((v & 0xf0f0f0f0) >> 4) | ((v & 0x0f0f0f0f) << 4));
+ v = (((v & 0xff00ff00) >> 8) | ((v & 0x00ff00ff) << 8));
+ return((v >> 16) | (v << 16));
+}
+
+static int findLSB (deUint32 value)
+{
+ for (int i = 0; i < 32; i++)
+ {
+ if (value & (1u<<i))
+ return i;
+ }
+ return -1;
+}
+
+static int findMSB (deInt32 value)
+{
+ if (value > 0)
+ return 31 - deClz32((deUint32)value);
+ else if (value < 0)
+ return 31 - deClz32(~(deUint32)value);
+ else
+ return -1;
+}
+
+static int findMSB (deUint32 value)
+{
+ if (value > 0)
+ return 31 - deClz32(value);
+ else
+ return -1;
+}
+
+static deUint32 toPrecision (deUint32 value, int numIntegerBits)
+{
+ return value & getLowBitMask(numIntegerBits);
+}
+
+static deInt32 toPrecision (deInt32 value, int numIntegerBits)
+{
+ return (deInt32)extendSignTo32((deUint32)value & getLowBitMask(numIntegerBits), numIntegerBits);
+}
+
+template<class TestClass>
+static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, bool intTypes, bool uintTypes, bool allPrec, deUint32 shaderBits)
+{
+ tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
+
+ parent->addChild(group);
+ const glu::DataType scalarTypes[] =
+ {
+ glu::TYPE_INT,
+ glu::TYPE_UINT
+ };
+
+ for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
+ {
+ const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
+
+ if ((!intTypes && scalarType == glu::TYPE_INT) || (!uintTypes && scalarType == glu::TYPE_UINT))
+ continue;
+
+ for (int vecSize = 1; vecSize <= 4; vecSize++)
+ {
+ for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
+ {
+ if (prec != glu::PRECISION_HIGHP && !allPrec)
+ continue;
+
+ for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
+ {
+ if (shaderBits & (1<<shaderTypeNdx))
+ group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
+ }
+ }
+ }
+ }
+}
+
+} // anonymous
+
+// IntegerFunctionCase
+
+class IntegerFunctionCase : public TestCase
+{
+public:
+ IntegerFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
+ ~IntegerFunctionCase (void);
+
+ virtual void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ m_executor->setShaderSources(programCollection);
+ }
+
+ virtual TestInstance* createInstance (Context& context) const = 0;
+ virtual void init (void);
+
+protected:
+ IntegerFunctionCase (const IntegerFunctionCase& other);
+ IntegerFunctionCase& operator= (const IntegerFunctionCase& other);
+
+ const glu::ShaderType m_shaderType;
+
+ ShaderSpec m_spec;
+
+ de::MovePtr<ShaderExecutor> m_executor;
+
+ const int m_numValues;
+};
+
+IntegerFunctionCase::IntegerFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
+ : TestCase (testCtx, name, description)
+ , m_shaderType (shaderType)
+ , m_executor (DE_NULL)
+ , m_numValues (100)
+{
+}
+
+IntegerFunctionCase::~IntegerFunctionCase (void)
+{
+}
+
+void IntegerFunctionCase::init (void)
+{
+ DE_ASSERT(!m_executor);
+
+ m_executor = de::MovePtr<ShaderExecutor>(createExecutor(m_shaderType, m_spec));
+ m_testCtx.getLog() << *m_executor;
+}
+
+// IntegerFunctionTestInstance
+
+class IntegerFunctionTestInstance : public TestInstance
+{
+public:
+ IntegerFunctionTestInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : TestInstance (context)
+ , m_shaderType (shaderType)
+ , m_spec (spec)
+ , m_numValues (numValues)
+ , m_name (name)
+ , m_executor (executor)
+ {
+ }
+ virtual tcu::TestStatus iterate (void);
+protected:
+ virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
+
+ virtual void getInputValues (int numValues, void* const* values) const = 0;
+
+ const glu::ShaderType m_shaderType;
+
+ ShaderSpec m_spec;
+
+ const int m_numValues;
+
+ const char* m_name;
+
+ std::ostringstream m_failMsg; //!< Comparison failure help message.
+
+ ShaderExecutor& m_executor;
+};
+
+tcu::TestStatus IntegerFunctionTestInstance::iterate (void)
+{
+ const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
+ const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
+ vector<deUint32> inputData (numInputScalars * m_numValues);
+ vector<deUint32> outputData (numOutputScalars * m_numValues);
+ const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
+ const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
+
+ // Initialize input data.
+ getInputValues(m_numValues, &inputPointers[0]);
+
+ // Execute shader.
+ m_executor.execute(m_context, m_numValues, &inputPointers[0], &outputPointers[0]);
+
+ // Compare results.
+ {
+ const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
+ const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
+ vector<void*> curInputPtr (inputPointers.size());
+ vector<void*> curOutputPtr (outputPointers.size());
+ int numFailed = 0;
+ tcu::TestContext& testCtx = m_context.getTestContext();
+ for (int valNdx = 0; valNdx < m_numValues; valNdx++)
+ {
+ // Set up pointers for comparison.
+ for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
+ curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
+
+ for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
+ curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
+
+ if (!compare(&curInputPtr[0], &curOutputPtr[0]))
+ {
+ // \todo [2013-08-08 pyry] We probably want to log reference value as well?
+
+ testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
+
+ testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
+ for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
+ testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
+ << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
+ << TestLog::EndMessage;
+
+ testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
+ for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
+ testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
+ << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
+ << TestLog::EndMessage;
+
+ m_failMsg.str("");
+ m_failMsg.clear();
+ numFailed += 1;
+ }
+ }
+
+ testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+ }
+}
+
+// Test cases
+
+class UaddCarryCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ UaddCarryCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x235facu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 integerMask = getLowBitMask(integerLength);
+ const bool isSigned = glu::isDataTypeIntOrIVec(type);
+ deUint32* in0 = (deUint32*)values[0];
+ deUint32* in1 = (deUint32*)values[1];
+
+ const struct
+ {
+ deUint32 x;
+ deUint32 y;
+ } easyCases[] =
+ {
+ { 0x00000000u, 0x00000000u },
+ { 0xfffffffeu, 0x00000001u },
+ { 0x00000001u, 0xfffffffeu },
+ { 0xffffffffu, 0x00000001u },
+ { 0x00000001u, 0xffffffffu },
+ { 0xfffffffeu, 0x00000002u },
+ { 0x00000002u, 0xfffffffeu },
+ { 0xffffffffu, 0xffffffffu }
+ };
+
+ // generate integers with proper bit count
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[easyCaseNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].x & integerMask;
+ in1[easyCaseNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].y & integerMask;
+ }
+ }
+
+ // convert to signed
+ if (isSigned)
+ {
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[easyCaseNdx*scalarSize + compNdx] = extendSignTo32(in0[easyCaseNdx*scalarSize + compNdx], integerLength);
+ in1[easyCaseNdx*scalarSize + compNdx] = extendSignTo32(in1[easyCaseNdx*scalarSize + compNdx], integerLength);
+ }
+ }
+ }
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, in0, numValues - DE_LENGTH_OF_ARRAY(easyCases));
+ generateRandomInputData(rnd, m_shaderType, type, precision, in1, numValues - DE_LENGTH_OF_ARRAY(easyCases));
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 mask0 = getLowBitMask(integerLength);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 in0 = ((const deUint32*)inputs[0])[compNdx];
+ const deUint32 in1 = ((const deUint32*)inputs[1])[compNdx];
+ const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 out1 = ((const deUint32*)outputs[1])[compNdx];
+ const deUint32 ref0 = in0+in1;
+ const deUint32 ref1 = (deUint64(in0)+deUint64(in1)) > 0xffffffffu ? 1u : 0u;
+
+ if (((out0&mask0) != (ref0&mask0)) || out1 != ref1)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref0) << ", " << tcu::toHex(ref1);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class UaddCarryCase : public IntegerFunctionCase
+{
+public:
+ UaddCarryCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "uaddCarry", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("x", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("y", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("sum", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("carry", glu::VarType(baseType, glu::PRECISION_LOWP)));
+ m_spec.source = "sum = uaddCarry(x, y, carry);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UaddCarryCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class UsubBorrowCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ UsubBorrowCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x235facu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 integerMask = getLowBitMask(integerLength);
+ const bool isSigned = glu::isDataTypeIntOrIVec(type);
+ deUint32* in0 = (deUint32*)values[0];
+ deUint32* in1 = (deUint32*)values[1];
+
+ const struct
+ {
+ deUint32 x;
+ deUint32 y;
+ } easyCases[] =
+ {
+ { 0x00000000u, 0x00000000u },
+ { 0x00000001u, 0x00000001u },
+ { 0x00000001u, 0x00000002u },
+ { 0x00000001u, 0xffffffffu },
+ { 0xfffffffeu, 0xffffffffu },
+ { 0xffffffffu, 0xffffffffu },
+ };
+
+ // generate integers with proper bit count
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[easyCaseNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].x & integerMask;
+ in1[easyCaseNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].y & integerMask;
+ }
+ }
+
+ // convert to signed
+ if (isSigned)
+ {
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[easyCaseNdx*scalarSize + compNdx] = extendSignTo32(in0[easyCaseNdx*scalarSize + compNdx], integerLength);
+ in1[easyCaseNdx*scalarSize + compNdx] = extendSignTo32(in1[easyCaseNdx*scalarSize + compNdx], integerLength);
+ }
+ }
+ }
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, in0, numValues - DE_LENGTH_OF_ARRAY(easyCases));
+ generateRandomInputData(rnd, m_shaderType, type, precision, in1, numValues - DE_LENGTH_OF_ARRAY(easyCases));
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 mask0 = getLowBitMask(integerLength);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 in0 = ((const deUint32*)inputs[0])[compNdx];
+ const deUint32 in1 = ((const deUint32*)inputs[1])[compNdx];
+ const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 out1 = ((const deUint32*)outputs[1])[compNdx];
+ const deUint32 ref0 = in0-in1;
+ const deUint32 ref1 = in0 >= in1 ? 0u : 1u;
+
+ if (((out0&mask0) != (ref0&mask0)) || out1 != ref1)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref0) << ", " << tcu::toHex(ref1);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class UsubBorrowCase : public IntegerFunctionCase
+{
+public:
+ UsubBorrowCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "usubBorrow", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("x", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("y", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("diff", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("carry", glu::VarType(baseType, glu::PRECISION_LOWP)));
+ m_spec.source = "diff = usubBorrow(x, y, carry);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UsubBorrowCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class UmulExtendedCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ UmulExtendedCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x235facu);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+// const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ deUint32* in0 = (deUint32*)values[0];
+ deUint32* in1 = (deUint32*)values[1];
+ int valueNdx = 0;
+
+ const struct
+ {
+ deUint32 x;
+ deUint32 y;
+ } easyCases[] =
+ {
+ { 0x00000000u, 0x00000000u },
+ { 0xffffffffu, 0x00000001u },
+ { 0xffffffffu, 0x00000002u },
+ { 0x00000001u, 0xffffffffu },
+ { 0x00000002u, 0xffffffffu },
+ { 0xffffffffu, 0xffffffffu },
+ };
+
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[valueNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].x;
+ in1[valueNdx*scalarSize + compNdx] = easyCases[easyCaseNdx].y;
+ }
+
+ valueNdx += 1;
+ }
+
+ while (valueNdx < numValues)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 base0 = rnd.getUint32();
+ const deUint32 base1 = rnd.getUint32();
+ const int adj0 = rnd.getInt(0, 20);
+ const int adj1 = rnd.getInt(0, 20);
+ in0[valueNdx*scalarSize + compNdx] = base0 >> adj0;
+ in1[valueNdx*scalarSize + compNdx] = base1 >> adj1;
+ }
+
+ valueNdx += 1;
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 in0 = ((const deUint32*)inputs[0])[compNdx];
+ const deUint32 in1 = ((const deUint32*)inputs[1])[compNdx];
+ const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 out1 = ((const deUint32*)outputs[1])[compNdx];
+ const deUint64 mul64 = deUint64(in0)*deUint64(in1);
+ const deUint32 ref0 = deUint32(mul64 >> 32);
+ const deUint32 ref1 = deUint32(mul64 & 0xffffffffu);
+
+ if (out0 != ref0 || out1 != ref1)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref0) << ", " << tcu::toHex(ref1);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class UmulExtendedCase : public IntegerFunctionCase
+{
+public:
+ UmulExtendedCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "umulExtended", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("x", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("y", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("msb", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("lsb", glu::VarType(baseType, precision)));
+ m_spec.source = "umulExtended(x, y, msb, lsb);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UmulExtendedCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class ImulExtendedCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ ImulExtendedCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x224fa1u);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+// const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ deUint32* in0 = (deUint32*)values[0];
+ deUint32* in1 = (deUint32*)values[1];
+ int valueNdx = 0;
+
+ const struct
+ {
+ deUint32 x;
+ deUint32 y;
+ } easyCases[] =
+ {
+ { 0x00000000u, 0x00000000u },
+ { 0xffffffffu, 0x00000002u },
+ { 0x7fffffffu, 0x00000001u },
+ { 0x7fffffffu, 0x00000002u },
+ { 0x7fffffffu, 0x7fffffffu },
+ { 0xffffffffu, 0xffffffffu },
+ { 0x7fffffffu, 0xfffffffeu },
+ };
+
+ for (int easyCaseNdx = 0; easyCaseNdx < DE_LENGTH_OF_ARRAY(easyCases); easyCaseNdx++)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ in0[valueNdx*scalarSize + compNdx] = (deInt32)easyCases[easyCaseNdx].x;
+ in1[valueNdx*scalarSize + compNdx] = (deInt32)easyCases[easyCaseNdx].y;
+ }
+
+ valueNdx += 1;
+ }
+
+ while (valueNdx < numValues)
+ {
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deInt32 base0 = (deInt32)rnd.getUint32();
+ const deInt32 base1 = (deInt32)rnd.getUint32();
+ const int adj0 = rnd.getInt(0, 20);
+ const int adj1 = rnd.getInt(0, 20);
+ in0[valueNdx*scalarSize + compNdx] = base0 >> adj0;
+ in1[valueNdx*scalarSize + compNdx] = base1 >> adj1;
+ }
+
+ valueNdx += 1;
+ }
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deInt32 in0 = ((const deInt32*)inputs[0])[compNdx];
+ const deInt32 in1 = ((const deInt32*)inputs[1])[compNdx];
+ const deInt32 out0 = ((const deInt32*)outputs[0])[compNdx];
+ const deInt32 out1 = ((const deInt32*)outputs[1])[compNdx];
+ const deInt64 mul64 = deInt64(in0)*deInt64(in1);
+ const deInt32 ref0 = deInt32(mul64 >> 32);
+ const deInt32 ref1 = deInt32(mul64 & 0xffffffffu);
+
+ if (out0 != ref0 || out1 != ref1)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref0) << ", " << tcu::toHex(ref1);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class ImulExtendedCase : public IntegerFunctionCase
+{
+public:
+ ImulExtendedCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "imulExtended", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("x", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("y", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("msb", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("lsb", glu::VarType(baseType, precision)));
+ m_spec.source = "imulExtended(x, y, msb, lsb);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new ImulExtendedCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class BitfieldExtractCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ BitfieldExtractCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xa113fca2u);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool ignoreSign = precision != glu::PRECISION_HIGHP && glu::isDataTypeIntOrIVec(type);
+ const int numBits = getShaderUintBitCount(m_shaderType, precision) - (ignoreSign ? 1 : 0);
+ deUint32* inValue = (deUint32*)values[0];
+ int* inOffset = (int*)values[1];
+ int* inBits = (int*)values[2];
+
+ for (int valueNdx = 0; valueNdx < numValues; ++valueNdx)
+ {
+ const int bits = rnd.getInt(0, numBits);
+ const int offset = rnd.getInt(0, numBits-bits);
+
+ inOffset[valueNdx] = offset;
+ inBits[valueNdx] = bits;
+ }
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inValue, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const bool isSigned = glu::isDataTypeIntOrIVec(type);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int offset = *((const int*)inputs[1]);
+ const int bits = *((const int*)inputs[2]);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 value = ((const deUint32*)inputs[0])[compNdx];
+ const deUint32 out = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 valMask = (bits == 32 ? ~0u : ((1u<<bits)-1u));
+ const deUint32 baseVal = (offset == 32) ? (0) : ((value >> offset) & valMask);
+ const deUint32 ref = baseVal | ((isSigned && (baseVal & (1<<(bits-1)))) ? ~valMask : 0u);
+
+ if (out != ref)
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class BitfieldExtractCase : public IntegerFunctionCase
+{
+public:
+ BitfieldExtractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "bitfieldExtract", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("value", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("offset", glu::VarType(glu::TYPE_INT, precision)));
+ m_spec.inputs.push_back(Symbol("bits", glu::VarType(glu::TYPE_INT, precision)));
+ m_spec.outputs.push_back(Symbol("extracted", glu::VarType(baseType, precision)));
+ m_spec.source = "extracted = bitfieldExtract(value, offset, bits);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new BitfieldExtractCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class BitfieldInsertCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ BitfieldInsertCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x12c2acff);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int numBits = getShaderUintBitCount(m_shaderType, precision);
+ deUint32* inBase = (deUint32*)values[0];
+ deUint32* inInsert = (deUint32*)values[1];
+ int* inOffset = (int*)values[2];
+ int* inBits = (int*)values[3];
+
+ for (int valueNdx = 0; valueNdx < numValues; ++valueNdx)
+ {
+ const int bits = rnd.getInt(0, numBits);
+ const int offset = rnd.getInt(0, numBits-bits);
+
+ inOffset[valueNdx] = offset;
+ inBits[valueNdx] = bits;
+ }
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inBase, numValues);
+ generateRandomInputData(rnd, m_shaderType, type, precision, inInsert, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 cmpMask = getLowBitMask(integerLength);
+ const int offset = *((const int*)inputs[2]);
+ const int bits = *((const int*)inputs[3]);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 base = ((const deUint32*)inputs[0])[compNdx];
+ const deUint32 insert = ((const deUint32*)inputs[1])[compNdx];
+ const deInt32 out = ((const deUint32*)outputs[0])[compNdx];
+
+ const deUint32 mask = bits == 32 ? ~0u : (1u<<bits)-1;
+ const deUint32 ref = (base & ~(mask<<offset)) | ((insert & mask)<<offset);
+
+ if ((out&cmpMask) != (ref&cmpMask))
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class BitfieldInsertCase : public IntegerFunctionCase
+{
+public:
+ BitfieldInsertCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "bitfieldInsert", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("base", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("insert", glu::VarType(baseType, precision)));
+ m_spec.inputs.push_back(Symbol("offset", glu::VarType(glu::TYPE_INT, precision)));
+ m_spec.inputs.push_back(Symbol("bits", glu::VarType(glu::TYPE_INT, precision)));
+ m_spec.outputs.push_back(Symbol("result", glu::VarType(baseType, precision)));
+ m_spec.source = "result = bitfieldInsert(base, insert, offset, bits);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new BitfieldInsertCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class BitfieldReverseCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ BitfieldReverseCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xff23a4);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ deUint32* inValue = (deUint32*)values[0];
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inValue, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const deUint32 cmpMask = reverseBits(getLowBitMask(integerLength));
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 value = ((const deUint32*)inputs[0])[compNdx];
+ const deInt32 out = ((const deUint32*)outputs[0])[compNdx];
+ const deUint32 ref = reverseBits(value);
+
+ if ((out&cmpMask) != (ref&cmpMask))
+ {
+ m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(ref);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class BitfieldReverseCase : public IntegerFunctionCase
+{
+public:
+ BitfieldReverseCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "bitfieldReverse", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("value", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("result", glu::VarType(baseType, glu::PRECISION_HIGHP)));
+ m_spec.source = "result = bitfieldReverse(value);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new BitfieldReverseCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class BitCountCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ BitCountCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0xab2cca4);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ deUint32* inValue = (deUint32*)values[0];
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inValue, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const deUint32 countMask = getLowBitMask(integerLength);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 value = ((const deUint32*)inputs[0])[compNdx];
+ const int out = ((const int*)outputs[0])[compNdx];
+ const int minRef = dePop32(value&countMask);
+ const int maxRef = dePop32(value);
+
+ if (!de::inRange(out, minRef, maxRef))
+ {
+ m_failMsg << "Expected [" << compNdx << "] in range [" << minRef << ", " << maxRef << "]";
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class BitCountCase : public IntegerFunctionCase
+{
+public:
+ BitCountCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "bitCount", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = vecSize == 1 ? glu::TYPE_INT : glu::getDataTypeIntVec(vecSize);
+
+ m_spec.inputs.push_back(Symbol("value", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("count", glu::VarType(intType, glu::PRECISION_LOWP)));
+ m_spec.source = "count = bitCount(value);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new BitCountCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class FindLSBCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ FindLSBCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x9923c2af);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ deUint32* inValue = (deUint32*)values[0];
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inValue, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+ const deUint32 mask = getLowBitMask(integerLength);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 value = ((const deUint32*)inputs[0])[compNdx];
+ const int out = ((const int*)outputs[0])[compNdx];
+ const int minRef = findLSB(value&mask);
+ const int maxRef = findLSB(value);
+
+ if (!de::inRange(out, minRef, maxRef))
+ {
+ m_failMsg << "Expected [" << compNdx << "] in range [" << minRef << ", " << maxRef << "]";
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class FindLSBCase : public IntegerFunctionCase
+{
+public:
+ FindLSBCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "findLSB", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = vecSize == 1 ? glu::TYPE_INT : glu::getDataTypeIntVec(vecSize);
+
+ m_spec.inputs.push_back(Symbol("value", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("lsb", glu::VarType(intType, glu::PRECISION_LOWP)));
+ m_spec.source = "lsb = findLSB(value);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new FindLSBCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+class findMSBCaseInstance : public IntegerFunctionTestInstance
+{
+public:
+ findMSBCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
+ : IntegerFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
+ {
+ }
+
+ void getInputValues (int numValues, void* const* values) const
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x742ac4e);
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ deUint32* inValue = (deUint32*)values[0];
+
+ generateRandomInputData(rnd, m_shaderType, type, precision, inValue, numValues);
+ }
+
+ bool compare (const void* const* inputs, const void* const* outputs)
+ {
+ const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
+ const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
+ const bool isSigned = glu::isDataTypeIntOrIVec(type);
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const int integerLength = getShaderUintBitCount(m_shaderType, precision);
+
+ for (int compNdx = 0; compNdx < scalarSize; compNdx++)
+ {
+ const deUint32 value = ((const deUint32*)inputs[0])[compNdx];
+ const int out = ((const deInt32*)outputs[0])[compNdx];
+ const int minRef = isSigned ? findMSB(toPrecision(deInt32(value), integerLength)) : findMSB(toPrecision(value, integerLength));
+ const int maxRef = isSigned ? findMSB(deInt32(value)) : findMSB(value);
+
+ if (!de::inRange(out, minRef, maxRef))
+ {
+ m_failMsg << "Expected [" << compNdx << "] in range [" << minRef << ", " << maxRef << "]";
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+class findMSBCase : public IntegerFunctionCase
+{
+public:
+ findMSBCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
+ : IntegerFunctionCase (testCtx, getIntegerFuncCaseName(baseType, precision, shaderType).c_str(), "findMSB", shaderType)
+ {
+ const int vecSize = glu::getDataTypeScalarSize(baseType);
+ const glu::DataType intType = vecSize == 1 ? glu::TYPE_INT : glu::getDataTypeIntVec(vecSize);
+
+ m_spec.inputs.push_back(Symbol("value", glu::VarType(baseType, precision)));
+ m_spec.outputs.push_back(Symbol("msb", glu::VarType(intType, glu::PRECISION_LOWP)));
+ m_spec.source = "msb = findMSB(value);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new findMSBCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
+ }
+};
+
+ShaderIntegerFunctionTests::ShaderIntegerFunctionTests (tcu::TestContext& testCtx)
+ : tcu::TestCaseGroup (testCtx, "integer", "Integer function tests")
+{
+}
+
+ShaderIntegerFunctionTests::~ShaderIntegerFunctionTests (void)
+{
+}
+
+void ShaderIntegerFunctionTests::init (void)
+{
+ enum
+ {
+ VS = (1<<glu::SHADERTYPE_VERTEX),
+ FS = (1<<glu::SHADERTYPE_FRAGMENT),
+ CS = (1<<glu::SHADERTYPE_COMPUTE),
+ GS = (1<<glu::SHADERTYPE_GEOMETRY),
+ TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
+ TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
+
+ ALL_SHADERS = VS|TC|TE|GS|FS|CS
+ };
+
+ // Int? Uint? AllPrec? Shaders
+ addFunctionCases<UaddCarryCase> (this, "uaddcarry", false, true, true, ALL_SHADERS);
+ addFunctionCases<UsubBorrowCase> (this, "usubborrow", false, true, true, ALL_SHADERS);
+ addFunctionCases<UmulExtendedCase> (this, "umulextended", false, true, false, ALL_SHADERS);
+ addFunctionCases<ImulExtendedCase> (this, "imulextended", true, false, false, ALL_SHADERS);
+ addFunctionCases<BitfieldExtractCase> (this, "bitfieldextract", true, true, true, ALL_SHADERS);
+ addFunctionCases<BitfieldInsertCase> (this, "bitfieldinsert", true, true, true, ALL_SHADERS);
+ addFunctionCases<BitfieldReverseCase> (this, "bitfieldreverse", true, true, true, ALL_SHADERS);
+ addFunctionCases<BitCountCase> (this, "bitcount", true, true, true, ALL_SHADERS);
+ addFunctionCases<FindLSBCase> (this, "findlsb", true, true, true, ALL_SHADERS);
+ addFunctionCases<findMSBCase> (this, "findMSB", true, true, true, ALL_SHADERS);
+}
+
+} // shaderexecutor
+} // vkt
--- /dev/null
+#ifndef _VKTSHADERINTEGERFUNCTIONTESTS_HPP
+#define _VKTSHADERINTEGERFUNCTIONTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Integer built-in function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+// ShaderIntegerFunctionTests
+
+class ShaderIntegerFunctionTests : public tcu::TestCaseGroup
+{
+public:
+ ShaderIntegerFunctionTests (tcu::TestContext& testCtx);
+ virtual ~ShaderIntegerFunctionTests (void);
+
+ virtual void init (void);
+
+private:
+ ShaderIntegerFunctionTests (const ShaderIntegerFunctionTests&); // not allowed!
+ ShaderIntegerFunctionTests& operator= (const ShaderIntegerFunctionTests&); // not allowed!
+};
+
+} // shaderexecutor
+} // vkt
+
+#endif // _VKTSHADERINTEGERFUNCTIONTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Floating-point packing and unpacking function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "vktShaderPackingFunctionTests.hpp"
+#include "vktShaderExecutor.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuFloat.hpp"
+#include "deRandom.hpp"
+#include "deMath.h"
+#include "deString.h"
+#include "deSharedPtr.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+using namespace shaderexecutor;
+
+using std::string;
+using tcu::TestLog;
+
+namespace
+{
+
+inline deUint32 getUlpDiff (float a, float b)
+{
+ const deUint32 aBits = tcu::Float32(a).bits();
+ const deUint32 bBits = tcu::Float32(b).bits();
+ return aBits > bBits ? aBits - bBits : bBits - aBits;
+}
+
+struct HexFloat
+{
+ const float value;
+ HexFloat (const float value_) : value(value_) {}
+};
+
+std::ostream& operator<< (std::ostream& str, const HexFloat& v)
+{
+ return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
+}
+
+static const char* getPrecisionPostfix (glu::Precision precision)
+{
+ static const char* s_postfix[] =
+ {
+ "_lowp",
+ "_mediump",
+ "_highp"
+ };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
+ DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[precision];
+}
+
+static const char* getShaderTypePostfix (glu::ShaderType shaderType)
+{
+ static const char* s_postfix[] =
+ {
+ "_vertex",
+ "_fragment",
+ "_geometry",
+ "_tess_control",
+ "_tess_eval",
+ "_compute"
+ };
+ DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
+ return s_postfix[shaderType];
+}
+
+} // anonymous
+
+// ShaderPackingFunctionCase
+
+class ShaderPackingFunctionCase : public TestCase
+{
+public:
+ ShaderPackingFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
+ ~ShaderPackingFunctionCase (void);
+
+ virtual void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ m_executor->setShaderSources(programCollection);
+ }
+ virtual TestInstance* createInstance (Context& context) const = 0;
+ void init (void);
+
+protected:
+ const glu::ShaderType m_shaderType;
+ ShaderSpec m_spec;
+ de::SharedPtr<ShaderExecutor> m_executor;
+
+private:
+ ShaderPackingFunctionCase (const ShaderPackingFunctionCase& other);
+ ShaderPackingFunctionCase& operator= (const ShaderPackingFunctionCase& other);
+};
+
+ShaderPackingFunctionCase::ShaderPackingFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
+ : TestCase (testCtx, name, description)
+ , m_shaderType (shaderType)
+ , m_executor (DE_NULL)
+{
+}
+
+ShaderPackingFunctionCase::~ShaderPackingFunctionCase (void)
+{
+}
+
+void ShaderPackingFunctionCase::init (void)
+{
+ DE_ASSERT(!m_executor);
+
+ m_executor = de::SharedPtr<ShaderExecutor>(createExecutor(m_shaderType, m_spec));
+ m_testCtx.getLog() << *m_executor;
+}
+
+// ShaderPackingFunctionTestInstance
+
+class ShaderPackingFunctionTestInstance : public TestInstance
+{
+public:
+ ShaderPackingFunctionTestInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : TestInstance (context)
+ , m_testCtx (context.getTestContext())
+ , m_shaderType (shaderType)
+ , m_spec (spec)
+ , m_name (name)
+ , m_executor (executor)
+ {
+ }
+ virtual tcu::TestStatus iterate (void) = 0;
+protected:
+ tcu::TestContext& m_testCtx;
+ const glu::ShaderType m_shaderType;
+ ShaderSpec m_spec;
+ const char* m_name;
+ de::SharedPtr<ShaderExecutor> m_executor;
+};
+
+// Test cases
+
+class PackSnorm2x16CaseInstance: public ShaderPackingFunctionTestInstance
+{
+public:
+ PackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, glu::Precision precision, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ , m_precision (precision)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<tcu::Vec2> inputs;
+ std::vector<deUint32> outputs;
+ const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
+ m_precision == glu::PRECISION_MEDIUMP ? 33 : // (2^-10) * (2^15) + 1
+ m_precision == glu::PRECISION_LOWP ? 129 : 0; // (2^-8) * (2^15) + 1
+
+ // Special values to check.
+ inputs.push_back(tcu::Vec2(0.0f, 0.0f));
+ inputs.push_back(tcu::Vec2(-1.0f, 1.0f));
+ inputs.push_back(tcu::Vec2(0.5f, -0.5f));
+ inputs.push_back(tcu::Vec2(-1.5f, 1.5f));
+ inputs.push_back(tcu::Vec2(0.25f, -0.75f));
+
+ // Random values, mostly in range.
+ for (int ndx = 0; ndx < 15; ndx++)
+ {
+ const float x = rnd.getFloat()*2.5f - 1.25f;
+ const float y = rnd.getFloat()*2.5f - 1.25f;
+ inputs.push_back(tcu::Vec2(x, y));
+ }
+
+ // Large random values.
+ for (int ndx = 0; ndx < 80; ndx++)
+ {
+ const float x = rnd.getFloat()*1e6f - 0.5e6f;
+ const float y = rnd.getFloat()*1e6f - 0.5e6f;
+ inputs.push_back(tcu::Vec2(x, y));
+ }
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < numValues; valNdx++)
+ {
+ const deUint16 ref0 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
+ const deUint16 ref1 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
+ const deUint32 ref = (ref1 << 16) | ref0;
+ const deUint32 res = outputs[valNdx];
+ const deUint16 res0 = (deUint16)(res & 0xffff);
+ const deUint16 res1 = (deUint16)(res >> 16);
+ const int diff0 = de::abs((int)ref0 - (int)res0);
+ const int diff1 = de::abs((int)ref1 - (int)res1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
+ << ", expected packSnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
+ << ", got " << tcu::toHex(res)
+ << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class PackSnorm2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ PackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
+ : ShaderPackingFunctionCase (testCtx, (string("packsnorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm2x16", shaderType)
+ , m_precision (precision)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = packSnorm2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new PackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, m_executor, getName());
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class UnpackSnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ UnpackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const deUint32 maxDiff = 1; // Rounding error.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<deUint32> inputs;
+ std::vector<tcu::Vec2> outputs;
+
+ inputs.push_back(0x00000000u);
+ inputs.push_back(0x7fff8000u);
+ inputs.push_back(0x80007fffu);
+ inputs.push_back(0xffffffffu);
+ inputs.push_back(0x0001fffeu);
+
+ // Random values.
+ for (int ndx = 0; ndx < 95; ndx++)
+ inputs.push_back(rnd.getUint32());
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deInt16 in0 = (deInt16)(deUint16)(inputs[valNdx] & 0xffff);
+ const deInt16 in1 = (deInt16)(deUint16)(inputs[valNdx] >> 16);
+ const float ref0 = de::clamp(float(in0) / 32767.f, -1.0f, 1.0f);
+ const float ref1 = de::clamp(float(in1) / 32767.f, -1.0f, 1.0f);
+ const float res0 = outputs[valNdx].x();
+ const float res1 = outputs[valNdx].y();
+
+ const deUint32 diff0 = getUlpDiff(ref0, res0);
+ const deUint32 diff1 = getUlpDiff(ref1, res1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
+ << " expected unpackSnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
+ << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
+ << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
+ << "\n ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ UnpackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase (testCtx, (string("unpacksnorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm2x16", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = unpackSnorm2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UnpackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+};
+
+class PackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ PackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, glu::Precision precision, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ , m_precision (precision)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<tcu::Vec2> inputs;
+ std::vector<deUint32> outputs;
+ const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
+ m_precision == glu::PRECISION_MEDIUMP ? 65 : // (2^-10) * (2^16) + 1
+ m_precision == glu::PRECISION_LOWP ? 257 : 0; // (2^-8) * (2^16) + 1
+
+ // Special values to check.
+ inputs.push_back(tcu::Vec2(0.0f, 0.0f));
+ inputs.push_back(tcu::Vec2(0.5f, 1.0f));
+ inputs.push_back(tcu::Vec2(1.0f, 0.5f));
+ inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
+ inputs.push_back(tcu::Vec2(0.25f, 0.75f));
+
+ // Random values, mostly in range.
+ for (int ndx = 0; ndx < 15; ndx++)
+ {
+ const float x = rnd.getFloat()*1.25f;
+ const float y = rnd.getFloat()*1.25f;
+ inputs.push_back(tcu::Vec2(x, y));
+ }
+
+ // Large random values.
+ for (int ndx = 0; ndx < 80; ndx++)
+ {
+ const float x = rnd.getFloat()*1e6f - 1e5f;
+ const float y = rnd.getFloat()*1e6f - 1e5f;
+ inputs.push_back(tcu::Vec2(x, y));
+ }
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint16 ref0 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
+ const deUint16 ref1 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
+ const deUint32 ref = (ref1 << 16) | ref0;
+ const deUint32 res = outputs[valNdx];
+ const deUint16 res0 = (deUint16)(res & 0xffff);
+ const deUint16 res1 = (deUint16)(res >> 16);
+ const int diff0 = de::abs((int)ref0 - (int)res0);
+ const int diff1 = de::abs((int)ref1 - (int)res1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
+ << ", expected packUnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
+ << ", got " << tcu::toHex(res)
+ << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class PackUnorm2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ PackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
+ : ShaderPackingFunctionCase (testCtx, (string("packunorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm2x16", shaderType)
+ , m_precision (precision)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = packUnorm2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new PackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, m_executor, getName());
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class UnpackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ UnpackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const deUint32 maxDiff = 1; // Rounding error.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<deUint32> inputs;
+ std::vector<tcu::Vec2> outputs;
+
+ inputs.push_back(0x00000000u);
+ inputs.push_back(0x7fff8000u);
+ inputs.push_back(0x80007fffu);
+ inputs.push_back(0xffffffffu);
+ inputs.push_back(0x0001fffeu);
+
+ // Random values.
+ for (int ndx = 0; ndx < 95; ndx++)
+ inputs.push_back(rnd.getUint32());
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint16 in0 = (deUint16)(inputs[valNdx] & 0xffff);
+ const deUint16 in1 = (deUint16)(inputs[valNdx] >> 16);
+ const float ref0 = float(in0) / 65535.0f;
+ const float ref1 = float(in1) / 65535.0f;
+ const float res0 = outputs[valNdx].x();
+ const float res1 = outputs[valNdx].y();
+
+ const deUint32 diff0 = getUlpDiff(ref0, res0);
+ const deUint32 diff1 = getUlpDiff(ref1, res1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
+ << " expected unpackUnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
+ << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
+ << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
+ << "\n ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+
+class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ UnpackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase(testCtx, (string("unpackunorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm2x16", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = unpackUnorm2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UnpackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+
+};
+
+class PackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ PackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const int maxDiff = 0; // Values can be represented exactly in mediump.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<tcu::Vec2> inputs;
+ std::vector<deUint32> outputs;
+
+ // Special values to check.
+ inputs.push_back(tcu::Vec2(0.0f, 0.0f));
+ inputs.push_back(tcu::Vec2(0.5f, 1.0f));
+ inputs.push_back(tcu::Vec2(1.0f, 0.5f));
+ inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
+ inputs.push_back(tcu::Vec2(0.25f, 0.75f));
+
+ // Random values.
+ {
+ const int minExp = -14;
+ const int maxExp = 15;
+
+ for (int ndx = 0; ndx < 95; ndx++)
+ {
+ tcu::Vec2 v;
+ for (int c = 0; c < 2; c++)
+ {
+ const int s = rnd.getBool() ? 1 : -1;
+ const int exp = rnd.getInt(minExp, maxExp);
+ const deUint32 mantissa = rnd.getUint32() & ((1<<23)-1);
+
+ v[c] = tcu::Float32::construct(s, exp ? exp : 1 /* avoid denormals */, (1u<<23) | mantissa).asFloat();
+ }
+ inputs.push_back(v);
+ }
+ }
+
+ // Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
+ for (std::vector<tcu::Vec2>::iterator inVal = inputs.begin(); inVal != inputs.end(); ++inVal)
+ *inVal = tcu::Vec2(tcu::Float16(inVal->x()).asFloat(), tcu::Float16(inVal->y()).asFloat());
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint16 ref0 = (deUint16)tcu::Float16(inputs[valNdx].x()).bits();
+ const deUint16 ref1 = (deUint16)tcu::Float16(inputs[valNdx].y()).bits();
+ const deUint32 ref = (ref1 << 16) | ref0;
+ const deUint32 res = outputs[valNdx];
+ const deUint16 res0 = (deUint16)(res & 0xffff);
+ const deUint16 res1 = (deUint16)(res >> 16);
+ const int diff0 = de::abs((int)ref0 - (int)res0);
+ const int diff1 = de::abs((int)ref1 - (int)res1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
+ << ", expected packHalf2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
+ << ", got " << tcu::toHex(res)
+ << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+class PackHalf2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ PackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase (testCtx, (string("packhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "packHalf2x16", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = packHalf2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new PackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+
+};
+
+class UnpackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ UnpackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const int maxDiff = 0; // All bits must be accurate.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<deUint32> inputs;
+ std::vector<tcu::Vec2> outputs;
+
+ // Special values.
+ inputs.push_back((tcu::Float16( 0.0f).bits() << 16) | tcu::Float16( 1.0f).bits());
+ inputs.push_back((tcu::Float16( 1.0f).bits() << 16) | tcu::Float16( 0.0f).bits());
+ inputs.push_back((tcu::Float16(-1.0f).bits() << 16) | tcu::Float16( 0.5f).bits());
+ inputs.push_back((tcu::Float16( 0.5f).bits() << 16) | tcu::Float16(-0.5f).bits());
+
+ // Construct random values.
+ {
+ const int minExp = -14;
+ const int maxExp = 15;
+ const int mantBits = 10;
+
+ for (int ndx = 0; ndx < 96; ndx++)
+ {
+ deUint32 inVal = 0;
+ for (int c = 0; c < 2; c++)
+ {
+ const int s = rnd.getBool() ? 1 : -1;
+ const int exp = rnd.getInt(minExp, maxExp);
+ const deUint32 mantissa = rnd.getUint32() & ((1<<mantBits)-1);
+ const deUint16 value = tcu::Float16::construct(s, exp ? exp : 1 /* avoid denorm */, (deUint16)((1u<<10) | mantissa)).bits();
+
+ inVal |= value << (16*c);
+ }
+ inputs.push_back(inVal);
+ }
+ }
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint16 in0 = (deUint16)(inputs[valNdx] & 0xffff);
+ const deUint16 in1 = (deUint16)(inputs[valNdx] >> 16);
+ const float ref0 = tcu::Float16(in0).asFloat();
+ const float ref1 = tcu::Float16(in1).asFloat();
+ const float res0 = outputs[valNdx].x();
+ const float res1 = outputs[valNdx].y();
+
+ const deUint32 refBits0 = tcu::Float32(ref0).bits();
+ const deUint32 refBits1 = tcu::Float32(ref1).bits();
+ const deUint32 resBits0 = tcu::Float32(res0).bits();
+ const deUint32 resBits1 = tcu::Float32(res1).bits();
+
+ const int diff0 = de::abs((int)refBits0 - (int)resBits0);
+ const int diff1 = de::abs((int)refBits1 - (int)resBits1);
+
+ if (diff0 > maxDiff || diff1 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
+ << " expected unpackHalf2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
+ << "vec2(" << ref0 << " / " << tcu::toHex(refBits0) << ", " << ref1 << " / " << tcu::toHex(refBits1) << ")"
+ << ", got vec2(" << res0 << " / " << tcu::toHex(resBits0) << ", " << res1 << " / " << tcu::toHex(resBits1) << ")"
+ << "\n ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+class UnpackHalf2x16Case : public ShaderPackingFunctionCase
+{
+public:
+ UnpackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase (testCtx, (string("unpackhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackHalf2x16", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP)));
+
+ m_spec.source = "out0 = unpackHalf2x16(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UnpackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+
+};
+
+class PackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ PackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, glu::Precision precision, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ , m_precision (precision)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x42f2c0);
+ std::vector<tcu::Vec4> inputs;
+ std::vector<deUint32> outputs;
+ const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
+ m_precision == glu::PRECISION_MEDIUMP ? 1 : // (2^-10) * (2^7) + 1
+ m_precision == glu::PRECISION_LOWP ? 2 : 0; // (2^-8) * (2^7) + 1
+
+ // Special values to check.
+ inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
+ inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
+ inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
+ inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
+ inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
+
+ // Random values, mostly in range.
+ for (int ndx = 0; ndx < 15; ndx++)
+ {
+ const float x = rnd.getFloat()*2.5f - 1.25f;
+ const float y = rnd.getFloat()*2.5f - 1.25f;
+ const float z = rnd.getFloat()*2.5f - 1.25f;
+ const float w = rnd.getFloat()*2.5f - 1.25f;
+ inputs.push_back(tcu::Vec4(x, y, z, w));
+ }
+
+ // Large random values.
+ for (int ndx = 0; ndx < 80; ndx++)
+ {
+ const float x = rnd.getFloat()*1e6f - 0.5e6f;
+ const float y = rnd.getFloat()*1e6f - 0.5e6f;
+ const float z = rnd.getFloat()*1e6f - 0.5e6f;
+ const float w = rnd.getFloat()*1e6f - 0.5e6f;
+ inputs.push_back(tcu::Vec4(x, y, z, w));
+ }
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < numValues; valNdx++)
+ {
+ const deUint16 ref0 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
+ const deUint16 ref1 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
+ const deUint16 ref2 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
+ const deUint16 ref3 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
+ const deUint32 ref = (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
+ const deUint32 res = outputs[valNdx];
+ const deUint16 res0 = (deUint8)(res & 0xff);
+ const deUint16 res1 = (deUint8)((res >> 8) & 0xff);
+ const deUint16 res2 = (deUint8)((res >> 16) & 0xff);
+ const deUint16 res3 = (deUint8)((res >> 24) & 0xff);
+ const int diff0 = de::abs((int)ref0 - (int)res0);
+ const int diff1 = de::abs((int)ref1 - (int)res1);
+ const int diff2 = de::abs((int)ref2 - (int)res2);
+ const int diff3 = de::abs((int)ref3 - (int)res3);
+
+ if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
+ << ", expected packSnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
+ << ", got " << tcu::toHex(res)
+ << "\n diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class PackSnorm4x8Case : public ShaderPackingFunctionCase
+{
+public:
+ PackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
+ : ShaderPackingFunctionCase (testCtx, (string("packsnorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm4x8", shaderType)
+ , m_precision (precision)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = packSnorm4x8(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new PackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, m_executor, getName());
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class UnpackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ UnpackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const deUint32 maxDiff = 1; // Rounding error.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<deUint32> inputs;
+ std::vector<tcu::Vec4> outputs;
+
+ inputs.push_back(0x00000000u);
+ inputs.push_back(0x7fff8000u);
+ inputs.push_back(0x80007fffu);
+ inputs.push_back(0xffffffffu);
+ inputs.push_back(0x0001fffeu);
+
+ // Random values.
+ for (int ndx = 0; ndx < 95; ndx++)
+ inputs.push_back(rnd.getUint32());
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deInt8 in0 = (deInt8)(deUint8)(inputs[valNdx] & 0xff);
+ const deInt8 in1 = (deInt8)(deUint8)((inputs[valNdx] >> 8) & 0xff);
+ const deInt8 in2 = (deInt8)(deUint8)((inputs[valNdx] >> 16) & 0xff);
+ const deInt8 in3 = (deInt8)(deUint8)(inputs[valNdx] >> 24);
+ const float ref0 = de::clamp(float(in0) / 127.f, -1.0f, 1.0f);
+ const float ref1 = de::clamp(float(in1) / 127.f, -1.0f, 1.0f);
+ const float ref2 = de::clamp(float(in2) / 127.f, -1.0f, 1.0f);
+ const float ref3 = de::clamp(float(in3) / 127.f, -1.0f, 1.0f);
+ const float res0 = outputs[valNdx].x();
+ const float res1 = outputs[valNdx].y();
+ const float res2 = outputs[valNdx].z();
+ const float res3 = outputs[valNdx].w();
+
+ const deUint32 diff0 = getUlpDiff(ref0, res0);
+ const deUint32 diff1 = getUlpDiff(ref1, res1);
+ const deUint32 diff2 = getUlpDiff(ref2, res2);
+ const deUint32 diff3 = getUlpDiff(ref3, res3);
+
+ if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
+ << " expected unpackSnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
+ << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
+ << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
+ << "\n ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+
+class UnpackSnorm4x8Case : public ShaderPackingFunctionCase
+{
+public:
+ UnpackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase (testCtx, (string("unpacksnorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm4x8", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = unpackSnorm4x8(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UnpackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+
+};
+
+class PackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ PackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, glu::Precision precision, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ , m_precision (precision)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<tcu::Vec4> inputs;
+ std::vector<deUint32> outputs;
+ const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
+ m_precision == glu::PRECISION_MEDIUMP ? 1 : // (2^-10) * (2^8) + 1
+ m_precision == glu::PRECISION_LOWP ? 2 : 0; // (2^-8) * (2^8) + 1
+
+ // Special values to check.
+ inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
+ inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
+ inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
+ inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
+ inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
+
+ // Random values, mostly in range.
+ for (int ndx = 0; ndx < 15; ndx++)
+ {
+ const float x = rnd.getFloat()*1.25f - 0.125f;
+ const float y = rnd.getFloat()*1.25f - 0.125f;
+ const float z = rnd.getFloat()*1.25f - 0.125f;
+ const float w = rnd.getFloat()*1.25f - 0.125f;
+ inputs.push_back(tcu::Vec4(x, y, z, w));
+ }
+
+ // Large random values.
+ for (int ndx = 0; ndx < 80; ndx++)
+ {
+ const float x = rnd.getFloat()*1e6f - 1e5f;
+ const float y = rnd.getFloat()*1e6f - 1e5f;
+ const float z = rnd.getFloat()*1e6f - 1e5f;
+ const float w = rnd.getFloat()*1e6f - 1e5f;
+ inputs.push_back(tcu::Vec4(x, y, z, w));
+ }
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint16 ref0 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
+ const deUint16 ref1 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
+ const deUint16 ref2 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
+ const deUint16 ref3 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
+ const deUint32 ref = (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
+ const deUint32 res = outputs[valNdx];
+ const deUint16 res0 = (deUint8)(res & 0xff);
+ const deUint16 res1 = (deUint8)((res >> 8) & 0xff);
+ const deUint16 res2 = (deUint8)((res >> 16) & 0xff);
+ const deUint16 res3 = (deUint8)((res >> 24) & 0xff);
+ const int diff0 = de::abs((int)ref0 - (int)res0);
+ const int diff1 = de::abs((int)ref1 - (int)res1);
+ const int diff2 = de::abs((int)ref2 - (int)res2);
+ const int diff3 = de::abs((int)ref3 - (int)res3);
+
+ if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
+ << ", expected packUnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
+ << ", got " << tcu::toHex(res)
+ << "\n diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class PackUnorm4x8Case : public ShaderPackingFunctionCase
+{
+public:
+ PackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
+ : ShaderPackingFunctionCase (testCtx, (string("packunorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm4x8", shaderType)
+ , m_precision (precision)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = packUnorm4x8(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new PackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, m_executor, getName());
+ }
+
+private:
+ const glu::Precision m_precision;
+};
+
+class UnpackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
+{
+public:
+ UnpackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, de::SharedPtr<ShaderExecutor> executor, const char* name)
+ : ShaderPackingFunctionTestInstance (context, shaderType, spec, executor, name)
+ {
+ }
+
+ tcu::TestStatus iterate (void)
+ {
+ const deUint32 maxDiff = 1; // Rounding error.
+ de::Random rnd (deStringHash(m_name) ^ 0x776002);
+ std::vector<deUint32> inputs;
+ std::vector<tcu::Vec4> outputs;
+
+ inputs.push_back(0x00000000u);
+ inputs.push_back(0x7fff8000u);
+ inputs.push_back(0x80007fffu);
+ inputs.push_back(0xffffffffu);
+ inputs.push_back(0x0001fffeu);
+
+ // Random values.
+ for (int ndx = 0; ndx < 95; ndx++)
+ inputs.push_back(rnd.getUint32());
+
+ outputs.resize(inputs.size());
+
+ m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
+
+ {
+ const void* in = &inputs[0];
+ void* out = &outputs[0];
+
+ m_executor->execute(m_context, (int)inputs.size(), &in, &out);
+ }
+
+ // Verify
+ {
+ const int numValues = (int)inputs.size();
+ const int maxPrints = 10;
+ int numFailed = 0;
+
+ for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
+ {
+ const deUint8 in0 = (deUint8)(inputs[valNdx] & 0xff);
+ const deUint8 in1 = (deUint8)((inputs[valNdx] >> 8) & 0xff);
+ const deUint8 in2 = (deUint8)((inputs[valNdx] >> 16) & 0xff);
+ const deUint8 in3 = (deUint8)(inputs[valNdx] >> 24);
+ const float ref0 = de::clamp(float(in0) / 255.f, 0.0f, 1.0f);
+ const float ref1 = de::clamp(float(in1) / 255.f, 0.0f, 1.0f);
+ const float ref2 = de::clamp(float(in2) / 255.f, 0.0f, 1.0f);
+ const float ref3 = de::clamp(float(in3) / 255.f, 0.0f, 1.0f);
+ const float res0 = outputs[valNdx].x();
+ const float res1 = outputs[valNdx].y();
+ const float res2 = outputs[valNdx].z();
+ const float res3 = outputs[valNdx].w();
+
+ const deUint32 diff0 = getUlpDiff(ref0, res0);
+ const deUint32 diff1 = getUlpDiff(ref1, res1);
+ const deUint32 diff2 = getUlpDiff(ref2, res2);
+ const deUint32 diff3 = getUlpDiff(ref3, res3);
+
+ if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
+ {
+ if (numFailed < maxPrints)
+ {
+ m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
+ << " expected unpackUnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
+ << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
+ << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
+ << "\n ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
+ << TestLog::EndMessage;
+ }
+ else if (numFailed == maxPrints)
+ m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
+
+ numFailed += 1;
+ }
+ }
+
+ m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
+
+ if (numFailed == 0)
+ return tcu::TestStatus::pass("Pass");
+ else
+ return tcu::TestStatus::fail("Result comparison failed");
+
+ }
+ }
+};
+
+class UnpackUnorm4x8Case : public ShaderPackingFunctionCase
+{
+public:
+ UnpackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
+ : ShaderPackingFunctionCase (testCtx, (string("unpackunorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm4x8", shaderType)
+ {
+ m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
+ m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
+
+ m_spec.source = "out0 = unpackUnorm4x8(in0);";
+ init();
+ }
+
+ TestInstance* createInstance (Context& ctx) const
+ {
+ return new UnpackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_executor, getName());
+ }
+
+};
+
+ShaderPackingFunctionTests::ShaderPackingFunctionTests (tcu::TestContext& testCtx)
+ : tcu::TestCaseGroup (testCtx, "pack_unpack", "Floating-point pack and unpack function tests")
+{
+}
+
+ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
+{
+}
+
+void ShaderPackingFunctionTests::init (void)
+{
+ // New built-in functions in GLES 3.1
+ {
+ const glu::ShaderType allShaderTypes[] =
+ {
+ glu::SHADERTYPE_VERTEX,
+ glu::SHADERTYPE_TESSELLATION_CONTROL,
+ glu::SHADERTYPE_TESSELLATION_EVALUATION,
+ glu::SHADERTYPE_GEOMETRY,
+ glu::SHADERTYPE_FRAGMENT,
+ glu::SHADERTYPE_COMPUTE
+ };
+
+ // packSnorm4x8
+ for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
+ {
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
+ addChild(new PackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
+ }
+
+ // unpackSnorm4x8
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
+ addChild(new UnpackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
+
+ // packUnorm4x8
+ for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
+ {
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
+ addChild(new PackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
+ }
+
+ // unpackUnorm4x8
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
+ addChild(new UnpackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
+ }
+
+ // GLES 3 functions in new shader types.
+ {
+ const glu::ShaderType newShaderTypes[] =
+ {
+ glu::SHADERTYPE_GEOMETRY,
+ glu::SHADERTYPE_COMPUTE
+ };
+
+ // packSnorm2x16
+ for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
+ {
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new PackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
+ }
+
+ // unpackSnorm2x16
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new UnpackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
+
+ // packUnorm2x16
+ for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
+ {
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new PackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
+ }
+
+ // unpackUnorm2x16
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new UnpackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
+
+ // packHalf2x16
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new PackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
+
+ // unpackHalf2x16
+ for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
+ addChild(new UnpackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
+ }
+}
+
+} // shaderexecutor
+} // vkt
--- /dev/null
+#ifndef _VKTSHADERPACKINGFUNCTIONTESTS_HPP
+#define _VKTSHADERPACKINGFUNCTIONTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Floating-point packing and unpacking function tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace shaderexecutor
+{
+
+class ShaderPackingFunctionTests : public tcu::TestCaseGroup
+{
+public:
+ ShaderPackingFunctionTests (tcu::TestContext& testCtx);
+ virtual ~ShaderPackingFunctionTests (void);
+
+ virtual void init (void);
+
+private:
+ ShaderPackingFunctionTests (const ShaderPackingFunctionTests&); // not allowed!
+ ShaderPackingFunctionTests& operator= (const ShaderPackingFunctionTests&); // not allowed!
+};
+
+} // shaderexecutor
+} // vkt
+
+#endif // _VKTSHADERPACKINGFUNCTIONTESTS_HPP
#include "vktShaderRenderReturnTests.hpp"
#include "vktShaderRenderStructTests.hpp"
#include "vktShaderRenderSwitchTests.hpp"
+#include "vktShaderExecutorTests.hpp"
#include <vector>
#include <sstream>
glslTests->addChild(sr::createStructTests (testCtx));
glslTests->addChild(sr::createSwitchTests (testCtx));
+ // ShaderExecutor-based tests
+ glslTests->addChild(shaderexecutor::createTests(testCtx));
+
return glslTests.release();
}