457 lines
16 KiB
C++
457 lines
16 KiB
C++
/*-------------------------------------------------------------------------
|
|
* 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 Compute Shader Built-in variable tests.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es31fComputeShaderBuiltinVarTests.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 "deSharedPtr.hpp"
|
|
#include "deStringUtil.hpp"
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
|
|
#include <map>
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gles31
|
|
{
|
|
namespace Functional
|
|
{
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using std::map;
|
|
using tcu::TestLog;
|
|
using tcu::UVec3;
|
|
using tcu::IVec3;
|
|
|
|
using namespace glu;
|
|
|
|
template<typename T, int Size>
|
|
struct LexicalCompareVec
|
|
{
|
|
inline bool operator() (const tcu::Vector<T, Size>& a, const tcu::Vector<T, Size>& b) const
|
|
{
|
|
for (int ndx = 0; ndx < Size; ndx++)
|
|
{
|
|
if (a[ndx] < b[ndx])
|
|
return true;
|
|
else if (a[ndx] > b[ndx])
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
typedef de::SharedPtr<glu::ShaderProgram> ShaderProgramSp;
|
|
typedef std::map<tcu::UVec3, ShaderProgramSp, LexicalCompareVec<deUint32, 3> > LocalSizeProgramMap;
|
|
|
|
class ComputeBuiltinVarCase : public TestCase
|
|
{
|
|
public:
|
|
ComputeBuiltinVarCase (Context& context, const char* name, const char* varName, DataType varType);
|
|
~ComputeBuiltinVarCase (void);
|
|
|
|
void init (void);
|
|
void deinit (void);
|
|
IterateResult iterate (void);
|
|
|
|
virtual UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const = 0;
|
|
|
|
protected:
|
|
struct SubCase
|
|
{
|
|
UVec3 localSize;
|
|
UVec3 numWorkGroups;
|
|
|
|
SubCase (void) {}
|
|
SubCase (const UVec3& localSize_, const UVec3& numWorkGroups_) : localSize(localSize_), numWorkGroups(numWorkGroups_) {}
|
|
};
|
|
|
|
vector<SubCase> m_subCases;
|
|
|
|
private:
|
|
ComputeBuiltinVarCase (const ComputeBuiltinVarCase& other);
|
|
ComputeBuiltinVarCase& operator= (const ComputeBuiltinVarCase& other);
|
|
|
|
deUint32 getProgram (const UVec3& localSize);
|
|
|
|
const string m_varName;
|
|
const DataType m_varType;
|
|
|
|
LocalSizeProgramMap m_progMap;
|
|
int m_subCaseNdx;
|
|
};
|
|
|
|
ComputeBuiltinVarCase::ComputeBuiltinVarCase (Context& context, const char* name, const char* varName, DataType varType)
|
|
: TestCase (context, name, varName)
|
|
, m_varName (varName)
|
|
, m_varType (varType)
|
|
, m_subCaseNdx (0)
|
|
{
|
|
}
|
|
|
|
ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void)
|
|
{
|
|
ComputeBuiltinVarCase::deinit();
|
|
}
|
|
|
|
void ComputeBuiltinVarCase::init (void)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
|
m_subCaseNdx = 0;
|
|
}
|
|
|
|
void ComputeBuiltinVarCase::deinit (void)
|
|
{
|
|
m_progMap.clear();
|
|
}
|
|
|
|
static string genBuiltinVarSource (const string& varName, DataType varType, const UVec3& localSize)
|
|
{
|
|
std::ostringstream src;
|
|
|
|
src << "#version 310 es\n"
|
|
<< "layout (local_size_x = " << localSize.x() << ", local_size_y = " << localSize.y() << ", local_size_z = " << localSize.z() << ") in;\n"
|
|
<< "uniform highp uvec2 u_stride;\n"
|
|
<< "layout(binding = 0) buffer Output\n"
|
|
<< "{\n"
|
|
<< " " << glu::getDataTypeName(varType) << " result[];\n"
|
|
<< "} sb_out;\n"
|
|
<< "\n"
|
|
<< "void main (void)\n"
|
|
<< "{\n"
|
|
<< " highp uint offset = u_stride.x*gl_GlobalInvocationID.z + u_stride.y*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
|
|
<< " sb_out.result[offset] = " << varName << ";\n"
|
|
<< "}\n";
|
|
|
|
return src.str();
|
|
}
|
|
|
|
deUint32 ComputeBuiltinVarCase::getProgram (const UVec3& localSize)
|
|
{
|
|
LocalSizeProgramMap::const_iterator cachePos = m_progMap.find(localSize);
|
|
if (cachePos != m_progMap.end())
|
|
return cachePos->second->getProgram();
|
|
else
|
|
{
|
|
ShaderProgramSp program(new ShaderProgram(m_context.getRenderContext(),
|
|
ProgramSources() << ComputeSource(genBuiltinVarSource(m_varName, m_varType, localSize))));
|
|
|
|
// Log all compiled programs.
|
|
m_testCtx.getLog() << *program;
|
|
if (!program->isOk())
|
|
throw tcu::TestError("Compile failed");
|
|
|
|
m_progMap[localSize] = program;
|
|
return program->getProgram();
|
|
}
|
|
}
|
|
|
|
static inline UVec3 readResultVec (const deUint32* ptr, int numComps)
|
|
{
|
|
UVec3 res;
|
|
for (int ndx = 0; ndx < numComps; ndx++)
|
|
res[ndx] = ptr[ndx];
|
|
return res;
|
|
}
|
|
|
|
static inline bool compareComps (const UVec3& a, const UVec3& b, int numComps)
|
|
{
|
|
DE_ASSERT(numComps == 1 || numComps == 3);
|
|
return numComps == 3 ? tcu::allEqual(a, b) : a.x() == b.x();
|
|
}
|
|
|
|
struct LogComps
|
|
{
|
|
const UVec3& v;
|
|
int numComps;
|
|
|
|
LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {}
|
|
};
|
|
|
|
static inline std::ostream& operator<< (std::ostream& str, const LogComps& c)
|
|
{
|
|
DE_ASSERT(c.numComps == 1 || c.numComps == 3);
|
|
return c.numComps == 3 ? str << c.v : str << c.v.x();
|
|
}
|
|
|
|
ComputeBuiltinVarCase::IterateResult ComputeBuiltinVarCase::iterate (void)
|
|
{
|
|
const tcu::ScopedLogSection section (m_testCtx.getLog(), string("Iteration") + de::toString(m_subCaseNdx), string("Iteration ") + de::toString(m_subCaseNdx));
|
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
|
const SubCase& subCase = m_subCases[m_subCaseNdx];
|
|
const deUint32 program = getProgram(subCase.localSize);
|
|
|
|
const tcu::UVec3 globalSize = subCase.localSize*subCase.numWorkGroups;
|
|
const tcu::UVec2 stride (globalSize[0]*globalSize[1], globalSize[0]);
|
|
const deUint32 numInvocations = subCase.localSize[0]*subCase.localSize[1]*subCase.localSize[2]*subCase.numWorkGroups[0]*subCase.numWorkGroups[1]*subCase.numWorkGroups[2];
|
|
|
|
const deUint32 outVarIndex = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "Output.result");
|
|
const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarIndex);
|
|
const deUint32 bufferSize = numInvocations*outVarInfo.arrayStride;
|
|
Buffer outputBuffer (m_context.getRenderContext());
|
|
|
|
TCU_CHECK(outVarInfo.arraySize == 0); // Unsized variable.
|
|
|
|
m_testCtx.getLog() << TestLog::Message << "Number of work groups = " << subCase.numWorkGroups << TestLog::EndMessage
|
|
<< TestLog::Message << "Work group size = " << subCase.localSize << TestLog::EndMessage;
|
|
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)bufferSize, DE_NULL, GL_STREAM_READ);
|
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
|
|
|
|
gl.useProgram(program);
|
|
gl.uniform2uiv(gl.getUniformLocation(program, "u_stride"), 1, stride.getPtr());
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed");
|
|
|
|
gl.dispatchCompute(subCase.numWorkGroups[0], subCase.numWorkGroups[1], subCase.numWorkGroups[2]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
|
|
|
|
{
|
|
const void* ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, bufferSize, GL_MAP_READ_BIT);
|
|
int numFailed = 0;
|
|
const int numScalars = getDataTypeScalarSize(m_varType);
|
|
const int maxLogPrints = 10;
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed");
|
|
TCU_CHECK(ptr);
|
|
|
|
for (deUint32 groupZ = 0; groupZ < subCase.numWorkGroups.z(); groupZ++)
|
|
for (deUint32 groupY = 0; groupY < subCase.numWorkGroups.y(); groupY++)
|
|
for (deUint32 groupX = 0; groupX < subCase.numWorkGroups.x(); groupX++)
|
|
for (deUint32 localZ = 0; localZ < subCase.localSize.z(); localZ++)
|
|
for (deUint32 localY = 0; localY < subCase.localSize.y(); localY++)
|
|
for (deUint32 localX = 0; localX < subCase.localSize.x(); localX++)
|
|
{
|
|
const UVec3 refGroupID (groupX, groupY, groupZ);
|
|
const UVec3 refLocalID (localX, localY, localZ);
|
|
const UVec3 refGlobalID = refGroupID * subCase.localSize + refLocalID;
|
|
const deUint32 refOffset = stride.x()*refGlobalID.z() + stride.y()*refGlobalID.y() + refGlobalID.x();
|
|
const UVec3 refValue = computeReference(subCase.numWorkGroups, subCase.localSize, refGroupID, refLocalID);
|
|
|
|
const deUint32* resPtr = (const deUint32*)((const deUint8*)ptr + refOffset*outVarInfo.arrayStride);
|
|
const UVec3 resValue = readResultVec(resPtr, numScalars);
|
|
|
|
if (!compareComps(refValue, resValue, numScalars))
|
|
{
|
|
if (numFailed < maxLogPrints)
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed at offset " << refOffset
|
|
<< ": expected " << LogComps(refValue, numScalars)
|
|
<< ", got " << LogComps(resValue, numScalars)
|
|
<< TestLog::EndMessage;
|
|
else if (numFailed == maxLogPrints)
|
|
m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
|
|
|
|
numFailed += 1;
|
|
}
|
|
}
|
|
|
|
m_testCtx.getLog() << TestLog::Message << (numInvocations-numFailed) << " / " << numInvocations << " values passed" << TestLog::EndMessage;
|
|
|
|
if (numFailed > 0)
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Comparison failed");
|
|
|
|
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
}
|
|
|
|
m_subCaseNdx += 1;
|
|
return (m_subCaseNdx < (int)m_subCases.size() && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP;
|
|
}
|
|
|
|
// Test cases
|
|
|
|
class NumWorkGroupsCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
NumWorkGroupsCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "num_work_groups", "gl_NumWorkGroups", TYPE_UINT_VEC3)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(numWorkGroups);
|
|
DE_UNREF(workGroupSize);
|
|
DE_UNREF(workGroupID);
|
|
DE_UNREF(localInvocationID);
|
|
return numWorkGroups;
|
|
}
|
|
};
|
|
|
|
class WorkGroupSizeCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
WorkGroupSizeCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "work_group_size", "gl_WorkGroupSize", TYPE_UINT_VEC3)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3)));
|
|
m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5)));
|
|
m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(numWorkGroups);
|
|
DE_UNREF(workGroupID);
|
|
DE_UNREF(localInvocationID);
|
|
return workGroupSize;
|
|
}
|
|
};
|
|
|
|
class WorkGroupIDCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
WorkGroupIDCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "work_group_id", "gl_WorkGroupID", TYPE_UINT_VEC3)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(numWorkGroups);
|
|
DE_UNREF(workGroupSize);
|
|
DE_UNREF(localInvocationID);
|
|
return workGroupID;
|
|
}
|
|
};
|
|
|
|
class LocalInvocationIDCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
LocalInvocationIDCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "local_invocation_id", "gl_LocalInvocationID", TYPE_UINT_VEC3)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3)));
|
|
m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5)));
|
|
m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(numWorkGroups);
|
|
DE_UNREF(workGroupSize);
|
|
DE_UNREF(workGroupID);
|
|
return localInvocationID;
|
|
}
|
|
};
|
|
|
|
class GlobalInvocationIDCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
GlobalInvocationIDCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "global_invocation_id", "gl_GlobalInvocationID", TYPE_UINT_VEC3)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(numWorkGroups);
|
|
return workGroupID * workGroupSize + localInvocationID;
|
|
}
|
|
};
|
|
|
|
class LocalInvocationIndexCase : public ComputeBuiltinVarCase
|
|
{
|
|
public:
|
|
LocalInvocationIndexCase (Context& context)
|
|
: ComputeBuiltinVarCase(context, "local_invocation_index", "gl_LocalInvocationIndex", TYPE_UINT)
|
|
{
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
|
|
m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
|
|
m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
|
|
}
|
|
|
|
UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
|
|
{
|
|
DE_UNREF(workGroupID);
|
|
DE_UNREF(numWorkGroups);
|
|
return UVec3(localInvocationID.z()*workGroupSize.x()*workGroupSize.y() + localInvocationID.y()*workGroupSize.x() + localInvocationID.x(), 0, 0);
|
|
}
|
|
};
|
|
|
|
ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context)
|
|
: TestCaseGroup(context, "compute", "Compute Shader Builtin Variables")
|
|
{
|
|
}
|
|
|
|
ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void)
|
|
{
|
|
}
|
|
|
|
void ComputeShaderBuiltinVarTests::init (void)
|
|
{
|
|
addChild(new NumWorkGroupsCase (m_context));
|
|
addChild(new WorkGroupSizeCase (m_context));
|
|
addChild(new WorkGroupIDCase (m_context));
|
|
addChild(new LocalInvocationIDCase (m_context));
|
|
addChild(new GlobalInvocationIDCase (m_context));
|
|
addChild(new LocalInvocationIndexCase (m_context));
|
|
}
|
|
|
|
} // Functional
|
|
} // gles31
|
|
} // deqp
|