You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1139 lines
42 KiB
1139 lines
42 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES 3.1 Module
|
|
* -------------------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Shader atomic operation tests.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es31fShaderAtomicOpTests.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "gluShaderUtil.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluObjectWrapper.hpp"
|
|
#include "gluProgramInterfaceQuery.hpp"
|
|
#include "tcuVector.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuVectorUtil.hpp"
|
|
#include "tcuFormatUtil.hpp"
|
|
#include "deStringUtil.hpp"
|
|
#include "deRandom.hpp"
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gles31
|
|
{
|
|
namespace Functional
|
|
{
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using tcu::TestLog;
|
|
using tcu::UVec3;
|
|
using std::set;
|
|
using namespace glu;
|
|
|
|
template<typename T, int Size>
|
|
static inline T product (const tcu::Vector<T, Size>& v)
|
|
{
|
|
T res = v[0];
|
|
for (int ndx = 1; ndx < Size; ndx++)
|
|
res *= v[ndx];
|
|
return res;
|
|
}
|
|
|
|
class ShaderAtomicOpCase : public TestCase
|
|
{
|
|
public:
|
|
ShaderAtomicOpCase (Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize);
|
|
~ShaderAtomicOpCase (void);
|
|
|
|
void init (void);
|
|
void deinit (void);
|
|
IterateResult iterate (void);
|
|
|
|
protected:
|
|
virtual void getInputs (int numValues, int stride, void* inputs) const = 0;
|
|
virtual bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const = 0;
|
|
|
|
const string m_funcName;
|
|
const AtomicOperandType m_operandType;
|
|
const DataType m_type;
|
|
const Precision m_precision;
|
|
|
|
const UVec3 m_workGroupSize;
|
|
const UVec3 m_numWorkGroups;
|
|
|
|
deUint32 m_initialValue;
|
|
|
|
private:
|
|
ShaderAtomicOpCase (const ShaderAtomicOpCase& other);
|
|
ShaderAtomicOpCase& operator= (const ShaderAtomicOpCase& other);
|
|
|
|
ShaderProgram* m_program;
|
|
};
|
|
|
|
ShaderAtomicOpCase::ShaderAtomicOpCase (Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize)
|
|
: TestCase (context, name, funcName)
|
|
, m_funcName (funcName)
|
|
, m_operandType (operandType)
|
|
, m_type (type)
|
|
, m_precision (precision)
|
|
, m_workGroupSize (workGroupSize)
|
|
, m_numWorkGroups (4,4,4)
|
|
, m_initialValue (0)
|
|
, m_program (DE_NULL)
|
|
{
|
|
}
|
|
|
|
ShaderAtomicOpCase::~ShaderAtomicOpCase (void)
|
|
{
|
|
ShaderAtomicOpCase::deinit();
|
|
}
|
|
|
|
void ShaderAtomicOpCase::init (void)
|
|
{
|
|
const bool isSSBO = m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
|
|
const char* precName = getPrecisionName(m_precision);
|
|
const char* typeName = getDataTypeName(m_type);
|
|
|
|
const DataType outType = isSSBO ? m_type : glu::TYPE_UINT;
|
|
const char* outTypeName = getDataTypeName(outType);
|
|
|
|
const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
|
|
std::ostringstream src;
|
|
|
|
src << glu::getGLSLVersionDeclaration(getContextTypeGLSLVersion(m_context.getRenderContext().getType())) << "\n"
|
|
<< "layout(local_size_x = " << m_workGroupSize.x()
|
|
<< ", local_size_y = " << m_workGroupSize.y()
|
|
<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
|
|
<< "layout(binding = 0) buffer InOut\n"
|
|
<< "{\n"
|
|
<< " " << precName << " " << typeName << " inputValues[" << numValues << "];\n"
|
|
<< " " << precName << " " << outTypeName << " outputValues[" << numValues << "];\n"
|
|
<< " " << (isSSBO ? "coherent " : "") << precName << " " << outTypeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
|
|
<< "} sb_inout;\n";
|
|
|
|
if (!isSSBO)
|
|
src << "shared " << precName << " " << typeName << " s_var;\n";
|
|
|
|
src << "\n"
|
|
<< "void main (void)\n"
|
|
<< "{\n"
|
|
<< " uint localSize = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
|
|
<< " uint globalNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
|
|
<< " uint globalOffs = localSize*globalNdx;\n"
|
|
<< " uint offset = globalOffs + gl_LocalInvocationIndex;\n"
|
|
<< "\n";
|
|
|
|
if (isSSBO)
|
|
{
|
|
DE_ASSERT(outType == m_type);
|
|
src << " sb_inout.outputValues[offset] = " << m_funcName << "(sb_inout.groupValues[globalNdx], sb_inout.inputValues[offset]);\n";
|
|
}
|
|
else
|
|
{
|
|
const string castBeg = outType != m_type ? (string(outTypeName) + "(") : string("");
|
|
const char* const castEnd = outType != m_type ? ")" : "";
|
|
|
|
src << " if (gl_LocalInvocationIndex == 0u)\n"
|
|
<< " s_var = " << typeName << "(" << tcu::toHex(m_initialValue) << "u);\n"
|
|
<< " barrier();\n"
|
|
<< " " << precName << " " << typeName << " res = " << m_funcName << "(s_var, sb_inout.inputValues[offset]);\n"
|
|
<< " sb_inout.outputValues[offset] = " << castBeg << "res" << castEnd << ";\n"
|
|
<< " barrier();\n"
|
|
<< " if (gl_LocalInvocationIndex == 0u)\n"
|
|
<< " sb_inout.groupValues[globalNdx] = " << castBeg << "s_var" << castEnd << ";\n";
|
|
}
|
|
|
|
src << "}\n";
|
|
|
|
DE_ASSERT(!m_program);
|
|
m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
|
|
|
|
m_testCtx.getLog() << *m_program;
|
|
|
|
if (!m_program->isOk())
|
|
{
|
|
delete m_program;
|
|
m_program = DE_NULL;
|
|
throw tcu::TestError("Compile failed");
|
|
}
|
|
}
|
|
|
|
void ShaderAtomicOpCase::deinit (void)
|
|
{
|
|
delete m_program;
|
|
m_program = DE_NULL;
|
|
}
|
|
|
|
ShaderAtomicOpCase::IterateResult ShaderAtomicOpCase::iterate (void)
|
|
{
|
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
|
const deUint32 program = m_program->getProgram();
|
|
const Buffer inoutBuffer (m_context.getRenderContext());
|
|
const deUint32 blockNdx = gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
|
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
|
|
const deUint32 inVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.inputValues[0]");
|
|
const InterfaceVariableInfo inVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, inVarNdx);
|
|
const deUint32 outVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
|
|
const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
|
|
const deUint32 groupVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
|
|
const InterfaceVariableInfo groupVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
|
|
const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
|
|
|
|
TCU_CHECK(inVarInfo.arraySize == numValues &&
|
|
outVarInfo.arraySize == numValues &&
|
|
groupVarInfo.arraySize == product(m_numWorkGroups));
|
|
|
|
gl.useProgram(program);
|
|
|
|
// Setup buffer.
|
|
{
|
|
vector<deUint8> bufData(blockInfo.dataSize);
|
|
std::fill(bufData.begin(), bufData.end(), 0);
|
|
|
|
getInputs((int)numValues, (int)inVarInfo.arrayStride, &bufData[0] + inVarInfo.offset);
|
|
|
|
if (m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE)
|
|
{
|
|
for (deUint32 valNdx = 0; valNdx < product(m_numWorkGroups); valNdx++)
|
|
*(deUint32*)(&bufData[0] + groupVarInfo.offset + groupVarInfo.arrayStride*valNdx) = m_initialValue;
|
|
}
|
|
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
|
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
|
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
|
}
|
|
|
|
gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
|
|
|
|
// Read back and compare
|
|
{
|
|
const void* resPtr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
|
|
bool isOk = true;
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
|
|
TCU_CHECK(resPtr);
|
|
|
|
isOk = verify((int)numValues,
|
|
(int)inVarInfo.arrayStride, (const deUint8*)resPtr + inVarInfo.offset,
|
|
(int)outVarInfo.arrayStride, (const deUint8*)resPtr + outVarInfo.offset,
|
|
(int)groupVarInfo.arrayStride, (const deUint8*)resPtr + groupVarInfo.offset);
|
|
|
|
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
|
|
|
|
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
|
|
isOk ? "Pass" : "Comparison failed");
|
|
}
|
|
|
|
return STOP;
|
|
}
|
|
|
|
class ShaderAtomicAddCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicAddCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicAdd", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
m_initialValue = 1;
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const int maxVal = m_precision == PRECISION_LOWP ? 2 : 32;
|
|
const int minVal = 1;
|
|
|
|
// \todo [2013-09-04 pyry] Negative values!
|
|
|
|
for (int valNdx = 0; valNdx < numValues; valNdx++)
|
|
*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const int groupOutput = *(const deInt32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
set<int> outValues;
|
|
bool maxFound = false;
|
|
int valueSum = (int)m_initialValue;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const int inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
valueSum += inputValue;
|
|
}
|
|
|
|
if (groupOutput != valueSum)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected sum " << valueSum << ", got " << groupOutput << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const int inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
const int outputValue = *(const deInt32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if (!de::inRange(outputValue, (int)m_initialValue, valueSum-inputValue))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": expected value in range [" << m_initialValue << ", " << (valueSum-inputValue)
|
|
<< "], got " << outputValue
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
if (outValues.find(outputValue) != outValues.end())
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found duplicate value " << outputValue
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
outValues.insert(outputValue);
|
|
if (outputValue == valueSum-inputValue)
|
|
maxFound = true;
|
|
}
|
|
|
|
if (!maxFound)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: could not find maximum expected value from group " << groupNdx << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
if (outValues.find((int)m_initialValue) == outValues.end())
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
static int getPrecisionNumIntegerBits (glu::Precision precision)
|
|
{
|
|
switch (precision)
|
|
{
|
|
case glu::PRECISION_HIGHP: return 32;
|
|
case glu::PRECISION_MEDIUMP: return 16;
|
|
case glu::PRECISION_LOWP: return 9;
|
|
default:
|
|
DE_ASSERT(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static deUint32 getPrecisionMask (int numPreciseBits)
|
|
{
|
|
// \note: bit shift with larger or equal than var length is undefined, use 64 bit ints
|
|
return (deUint32)((((deUint64)1u) << numPreciseBits) - 1) ;
|
|
}
|
|
|
|
static bool intEqualsAfterUintCast (deInt32 value, deUint32 casted, glu::Precision precision)
|
|
{
|
|
// Bit format of 'casted' = [ uint -> highp uint promotion bits (0) ] [ sign extend bits (s) ] [ value bits ]
|
|
// |--min len---|
|
|
// |---------------signed length---------|
|
|
// |-------------------------------- highp uint length ----------------------------|
|
|
|
|
const deUint32 reference = (deUint32)value;
|
|
const int signBitOn = value < 0;
|
|
const int numPreciseBits = getPrecisionNumIntegerBits(precision);
|
|
const deUint32 preciseMask = getPrecisionMask(numPreciseBits);
|
|
|
|
// Lowest N bits must match, N = minimum precision
|
|
if ((reference & preciseMask) != (casted & preciseMask))
|
|
return false;
|
|
|
|
// Other lowest bits must match the sign and the remaining (topmost) if any must be 0
|
|
for (int signedIntegerLength = numPreciseBits; signedIntegerLength <= 32; ++signedIntegerLength)
|
|
{
|
|
const deUint32 signBits = (signBitOn) ? (getPrecisionMask(signedIntegerLength)) : (0u);
|
|
|
|
if ((signBits & ~preciseMask) == (casted & ~preciseMask))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool containsAfterUintCast (const std::set<deInt32>& haystack, deUint32 needle, glu::Precision precision)
|
|
{
|
|
for (std::set<deInt32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
|
|
if (intEqualsAfterUintCast(*it, needle, precision))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool containsAfterUintCast (const std::set<deUint32>& haystack, deInt32 needle, glu::Precision precision)
|
|
{
|
|
for (std::set<deUint32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
|
|
if (intEqualsAfterUintCast(needle, *it, precision))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
class ShaderAtomicMinCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicMinCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicMin", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
m_initialValue = m_precision == PRECISION_LOWP ? 100 : 1000;
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const bool isSigned = m_type == TYPE_INT;
|
|
const int maxVal = m_precision == PRECISION_LOWP ? 100 : 1000;
|
|
const int minVal = isSigned ? -maxVal : 0;
|
|
|
|
for (int valNdx = 0; valNdx < numValues; valNdx++)
|
|
*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
bool anyError = false;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
set<deInt32> inValues;
|
|
set<deUint32> outValues;
|
|
int minValue = (int)m_initialValue;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
inValues.insert(inputValue);
|
|
minValue = de::min(inputValue, minValue);
|
|
}
|
|
|
|
if (!intEqualsAfterUintCast(minValue, groupOutput, m_precision))
|
|
{
|
|
m_testCtx.getLog()
|
|
<< TestLog::Message
|
|
<< "ERROR: at group " << groupNdx
|
|
<< ": expected minimum " << minValue << " (" << tcu::Format::Hex<8>((deUint32)minValue) << ")"
|
|
<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
|
|
<< TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
|
|
!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << outputValue
|
|
<< " (" << tcu::Format::Hex<8>(outputValue) << ")"
|
|
<< TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
|
|
outValues.insert(outputValue);
|
|
}
|
|
|
|
if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
}
|
|
|
|
return !anyError;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicMaxCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicMaxCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicMax", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
const bool isSigned = m_type == TYPE_INT;
|
|
m_initialValue = isSigned ? (m_precision == PRECISION_LOWP ? -100 : -1000) : 0;
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const bool isSigned = m_type == TYPE_INT;
|
|
const int maxVal = m_precision == PRECISION_LOWP ? 100 : 1000;
|
|
const int minVal = isSigned ? -maxVal : 0;
|
|
|
|
for (int valNdx = 0; valNdx < numValues; valNdx++)
|
|
*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
bool anyError = false;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
set<int> inValues;
|
|
set<deUint32> outValues;
|
|
int maxValue = (int)m_initialValue;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
inValues.insert(inputValue);
|
|
maxValue = de::max(maxValue, inputValue);
|
|
}
|
|
|
|
if (!intEqualsAfterUintCast(maxValue, groupOutput, m_precision))
|
|
{
|
|
m_testCtx.getLog()
|
|
<< TestLog::Message
|
|
<< "ERROR: at group " << groupNdx
|
|
<< ": expected maximum " << maxValue << " (" << tcu::Format::Hex<8>((deUint32)maxValue) << ")"
|
|
<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
|
|
<< TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
|
|
!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << outputValue
|
|
<< " (" << tcu::Format::Hex<8>(outputValue) << ")"
|
|
<< TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
|
|
outValues.insert(outputValue);
|
|
}
|
|
|
|
if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
|
|
anyError = true;
|
|
}
|
|
}
|
|
|
|
return !anyError;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicAndCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicAndCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicAnd", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
const deUint32 valueMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
|
|
m_initialValue = ~((1u<<(numBits-1u)) | 1u) & valueMask; // All bits except lowest and highest set.
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
const deUint32 valueMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupMask = 1<<rnd.getInt(0, numBits-2); // One bit is always set.
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = (rnd.getUint32() & valueMask) | groupMask;
|
|
}
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
const deUint32 compareMask = (m_type == TYPE_UINT || numBits == 32) ? ~0u : (1u<<numBits)-1u;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
deUint32 expectedValue = m_initialValue;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
expectedValue &= inputValue;
|
|
}
|
|
|
|
if ((groupOutput & compareMask) != (expectedValue & compareMask))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if ((compareMask & (outputValue & ~m_initialValue)) != 0)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << tcu::toHex(outputValue)
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicOrCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicOrCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicOr", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
m_initialValue = 1u; // Lowest bit set.
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = 1u<<rnd.getInt(0, numBits-1);
|
|
}
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
const deUint32 compareMask = (m_type == TYPE_UINT || numBits == 32) ? ~0u : (1u<<numBits)-1u;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
deUint32 expectedValue = m_initialValue;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
expectedValue |= inputValue;
|
|
}
|
|
|
|
if ((groupOutput & compareMask) != (expectedValue & compareMask))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if ((compareMask & (outputValue & m_initialValue)) == 0)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << tcu::toHex(outputValue)
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicXorCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicXorCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicXor", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
m_initialValue = 0;
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
de::Random rnd (deStringHash(getName()));
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
|
|
// First uses random bit-pattern.
|
|
*(deUint32*)((deUint8*)inputs + stride*(groupOffset)) = rnd.getUint32();
|
|
|
|
// Rest have either all or no bits set.
|
|
for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
|
|
*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = rnd.getBool() ? ~0u : 0u;
|
|
}
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
const int numBits = m_precision == PRECISION_HIGHP ? 32 :
|
|
m_precision == PRECISION_MEDIUMP ? 16 : 8;
|
|
const deUint32 compareMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
const deUint32 randomValue = *(const deInt32*)((const deUint8*)inputs + inputStride*groupOffset);
|
|
const deUint32 expected0 = randomValue ^ 0u;
|
|
const deUint32 expected1 = randomValue ^ ~0u;
|
|
int numXorZeros = (m_initialValue == 0) ? 1 : 0;
|
|
|
|
for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
|
|
if (inputValue == 0)
|
|
numXorZeros += 1;
|
|
}
|
|
|
|
const deUint32 expected = (numXorZeros%2 == 0) ? expected0 : expected1;
|
|
|
|
if ((groupOutput & compareMask) != (expected & compareMask))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expected0)
|
|
<< " or " << tcu::toHex(expected1) << " (compare mask " << tcu::toHex(compareMask)
|
|
<< "), got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if ((outputValue & compareMask) != 0 &&
|
|
(outputValue & compareMask) != compareMask &&
|
|
(outputValue & compareMask) != (expected0&compareMask) &&
|
|
(outputValue & compareMask) != (expected1&compareMask))
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << tcu::toHex(outputValue)
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
|
|
{
|
|
public:
|
|
ShaderAtomicExchangeCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: ShaderAtomicOpCase(context, name, "atomicExchange", operandType, type, precision, UVec3(3,2,1))
|
|
{
|
|
m_initialValue = 0;
|
|
}
|
|
|
|
protected:
|
|
void getInputs (int numValues, int stride, void* inputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
*(int*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = localNdx+1;
|
|
}
|
|
}
|
|
|
|
bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
|
|
{
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
const int numWorkGroups = numValues/workGroupSize;
|
|
|
|
DE_UNREF(inputStride && inputs);
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const int groupOutput = *(const deInt32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
|
|
set<int> usedValues;
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const int outputValue = *(const deInt32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
|
|
|
|
if (!de::inRange(outputValue, 0, workGroupSize) || usedValues.find(outputValue) != usedValues.end())
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": found unexpected value " << outputValue
|
|
<< TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
usedValues.insert(outputValue);
|
|
}
|
|
|
|
if (!de::inRange(groupOutput, 0, workGroupSize) || usedValues.find(groupOutput) != usedValues.end())
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": unexpected final value" << groupOutput << TestLog::EndMessage;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ShaderAtomicCompSwapCase : public TestCase
|
|
{
|
|
public:
|
|
ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision);
|
|
~ShaderAtomicCompSwapCase (void);
|
|
|
|
void init (void);
|
|
void deinit (void);
|
|
IterateResult iterate (void);
|
|
|
|
protected:
|
|
|
|
private:
|
|
ShaderAtomicCompSwapCase (const ShaderAtomicCompSwapCase& other);
|
|
ShaderAtomicCompSwapCase& operator= (const ShaderAtomicCompSwapCase& other);
|
|
|
|
const AtomicOperandType m_operandType;
|
|
const DataType m_type;
|
|
const Precision m_precision;
|
|
|
|
const UVec3 m_workGroupSize;
|
|
const UVec3 m_numWorkGroups;
|
|
|
|
ShaderProgram* m_program;
|
|
};
|
|
|
|
ShaderAtomicCompSwapCase::ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
|
|
: TestCase (context, name, "atomicCompSwap() Test")
|
|
, m_operandType (operandType)
|
|
, m_type (type)
|
|
, m_precision (precision)
|
|
, m_workGroupSize (3,2,1)
|
|
, m_numWorkGroups (4,4,4)
|
|
, m_program (DE_NULL)
|
|
{
|
|
}
|
|
|
|
ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
|
|
{
|
|
ShaderAtomicCompSwapCase::deinit();
|
|
}
|
|
|
|
void ShaderAtomicCompSwapCase::init (void)
|
|
{
|
|
const bool isSSBO = m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
|
|
const char* precName = getPrecisionName(m_precision);
|
|
const char* typeName = getDataTypeName(m_type);
|
|
const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
|
|
std::ostringstream src;
|
|
|
|
src << "#version 310 es\n"
|
|
<< "layout(local_size_x = " << m_workGroupSize.x()
|
|
<< ", local_size_y = " << m_workGroupSize.y()
|
|
<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
|
|
<< "layout(binding = 0) buffer InOut\n"
|
|
<< "{\n"
|
|
<< " " << precName << " " << typeName << " compareValues[" << numValues << "];\n"
|
|
<< " " << precName << " " << typeName << " exchangeValues[" << numValues << "];\n"
|
|
<< " " << precName << " " << typeName << " outputValues[" << numValues << "];\n"
|
|
<< " " << (isSSBO ? "coherent " : "") << precName << " " << typeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
|
|
<< "} sb_inout;\n";
|
|
|
|
if (!isSSBO)
|
|
src << "shared " << precName << " " << typeName << " s_var;\n";
|
|
|
|
src << "\n"
|
|
<< "void main (void)\n"
|
|
<< "{\n"
|
|
<< " uint localSize = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
|
|
<< " uint globalNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
|
|
<< " uint globalOffs = localSize*globalNdx;\n"
|
|
<< " uint offset = globalOffs + gl_LocalInvocationIndex;\n"
|
|
<< "\n";
|
|
|
|
if (!isSSBO)
|
|
{
|
|
src << " if (gl_LocalInvocationIndex == 0u)\n"
|
|
<< " s_var = " << typeName << "(" << 0 << ");\n"
|
|
<< "\n";
|
|
}
|
|
|
|
src << " " << precName << " " << typeName << " compare = sb_inout.compareValues[offset];\n"
|
|
<< " " << precName << " " << typeName << " exchange = sb_inout.exchangeValues[offset];\n"
|
|
<< " " << precName << " " << typeName << " result;\n"
|
|
<< " bool swapDone = false;\n"
|
|
<< "\n"
|
|
<< " for (uint ndx = 0u; ndx < localSize; ndx++)\n"
|
|
<< " {\n"
|
|
<< " barrier();\n"
|
|
<< " if (!swapDone)\n"
|
|
<< " {\n"
|
|
<< " result = atomicCompSwap(" << (isSSBO ? "sb_inout.groupValues[globalNdx]" : "s_var") << ", compare, exchange);\n"
|
|
<< " if (result == compare)\n"
|
|
<< " swapDone = true;\n"
|
|
<< " }\n"
|
|
<< " }\n"
|
|
<< "\n"
|
|
<< " sb_inout.outputValues[offset] = result;\n";
|
|
|
|
if (!isSSBO)
|
|
{
|
|
src << " barrier();\n"
|
|
<< " if (gl_LocalInvocationIndex == 0u)\n"
|
|
<< " sb_inout.groupValues[globalNdx] = s_var;\n";
|
|
}
|
|
|
|
src << "}\n";
|
|
|
|
DE_ASSERT(!m_program);
|
|
m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
|
|
|
|
m_testCtx.getLog() << *m_program;
|
|
|
|
if (!m_program->isOk())
|
|
{
|
|
delete m_program;
|
|
m_program = DE_NULL;
|
|
throw tcu::TestError("Compile failed");
|
|
}
|
|
}
|
|
|
|
void ShaderAtomicCompSwapCase::deinit (void)
|
|
{
|
|
delete m_program;
|
|
m_program = DE_NULL;
|
|
}
|
|
|
|
ShaderAtomicOpCase::IterateResult ShaderAtomicCompSwapCase::iterate (void)
|
|
{
|
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
|
const deUint32 program = m_program->getProgram();
|
|
const Buffer inoutBuffer (m_context.getRenderContext());
|
|
const deUint32 blockNdx = gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
|
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
|
|
const deUint32 cmpVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.compareValues[0]");
|
|
const InterfaceVariableInfo cmpVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, cmpVarNdx);
|
|
const deUint32 exhVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.exchangeValues[0]");
|
|
const InterfaceVariableInfo exhVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, exhVarNdx);
|
|
const deUint32 outVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
|
|
const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
|
|
const deUint32 groupVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
|
|
const InterfaceVariableInfo groupVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
|
|
const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
|
|
|
|
TCU_CHECK(cmpVarInfo.arraySize == numValues &&
|
|
exhVarInfo.arraySize == numValues &&
|
|
outVarInfo.arraySize == numValues &&
|
|
groupVarInfo.arraySize == product(m_numWorkGroups));
|
|
|
|
gl.useProgram(program);
|
|
|
|
// \todo [2013-09-05 pyry] Use randomized input values!
|
|
|
|
// Setup buffer.
|
|
{
|
|
const deUint32 workGroupSize = product(m_workGroupSize);
|
|
vector<deUint8> bufData (blockInfo.dataSize);
|
|
|
|
std::fill(bufData.begin(), bufData.end(), 0);
|
|
|
|
for (deUint32 ndx = 0; ndx < numValues; ndx++)
|
|
*(deUint32*)(&bufData[0] + cmpVarInfo.offset + cmpVarInfo.arrayStride*ndx) = ndx%workGroupSize;
|
|
|
|
for (deUint32 ndx = 0; ndx < numValues; ndx++)
|
|
*(deUint32*)(&bufData[0] + exhVarInfo.offset + exhVarInfo.arrayStride*ndx) = (ndx%workGroupSize)+1;
|
|
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
|
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
|
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
|
}
|
|
|
|
gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
|
|
|
|
// Read back and compare
|
|
{
|
|
const void* resPtr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
|
|
const int numWorkGroups = (int)product(m_numWorkGroups);
|
|
const int workGroupSize = (int)product(m_workGroupSize);
|
|
bool isOk = true;
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
|
|
TCU_CHECK(resPtr);
|
|
|
|
for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
|
|
{
|
|
const int groupOffset = groupNdx*workGroupSize;
|
|
const int groupOutput = *(const deInt32*)((const deUint8*)resPtr + groupVarInfo.offset + groupNdx*groupVarInfo.arrayStride);
|
|
|
|
for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
|
|
{
|
|
const int refValue = localNdx;
|
|
const int outputValue = *(const deInt32*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*(groupOffset+localNdx));
|
|
|
|
if (outputValue != refValue)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
|
|
<< ": expected " << refValue << ", got " << outputValue
|
|
<< TestLog::EndMessage;
|
|
isOk = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (groupOutput != workGroupSize)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected" << workGroupSize << ", got " << groupOutput << TestLog::EndMessage;
|
|
isOk = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
|
|
|
|
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
|
|
isOk ? "Pass" : "Comparison failed");
|
|
}
|
|
|
|
return STOP;
|
|
}
|
|
|
|
ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
|
|
: TestCaseGroup (context, name, "Atomic Operation Tests")
|
|
, m_operandType (operandType)
|
|
{
|
|
}
|
|
|
|
ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
|
|
{
|
|
}
|
|
|
|
template<typename T>
|
|
static tcu::TestCaseGroup* createAtomicOpGroup (Context& context, AtomicOperandType operandType, const char* groupName)
|
|
{
|
|
tcu::TestCaseGroup *const group = new tcu::TestCaseGroup(context.getTestContext(), groupName, (string("Atomic ") + groupName).c_str());
|
|
try
|
|
{
|
|
for (int precNdx = 0; precNdx < PRECISION_LAST; precNdx++)
|
|
{
|
|
for (int typeNdx = 0; typeNdx < 2; typeNdx++)
|
|
{
|
|
const Precision precision = Precision(precNdx);
|
|
const DataType type = typeNdx > 0 ? TYPE_INT : TYPE_UINT;
|
|
const string caseName = string(getPrecisionName(precision)) + "_" + getDataTypeName(type);
|
|
|
|
group->addChild(new T(context, caseName.c_str(), operandType, type, precision));
|
|
}
|
|
}
|
|
|
|
return group;
|
|
}
|
|
catch (...)
|
|
{
|
|
delete group;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void ShaderAtomicOpTests::init (void)
|
|
{
|
|
addChild(createAtomicOpGroup<ShaderAtomicAddCase> (m_context, m_operandType, "add"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicMinCase> (m_context, m_operandType, "min"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicMaxCase> (m_context, m_operandType, "max"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicAndCase> (m_context, m_operandType, "and"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicOrCase> (m_context, m_operandType, "or"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicXorCase> (m_context, m_operandType, "xor"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicExchangeCase> (m_context, m_operandType, "exchange"));
|
|
addChild(createAtomicOpGroup<ShaderAtomicCompSwapCase> (m_context, m_operandType, "compswap"));
|
|
}
|
|
|
|
} // Functional
|
|
} // gles31
|
|
} // deqp
|