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.
1687 lines
74 KiB
1687 lines
74 KiB
4 months ago
|
/*-------------------------------------------------------------------------
|
||
|
* 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 Basic Compute Shader Tests.
|
||
|
*//*--------------------------------------------------------------------*/
|
||
|
|
||
|
#include "es31fBasicComputeShaderTests.hpp"
|
||
|
#include "gluShaderProgram.hpp"
|
||
|
#include "gluObjectWrapper.hpp"
|
||
|
#include "gluRenderContext.hpp"
|
||
|
#include "gluProgramInterfaceQuery.hpp"
|
||
|
#include "gluContextInfo.hpp"
|
||
|
#include "glwFunctions.hpp"
|
||
|
#include "glwEnums.hpp"
|
||
|
#include "tcuTestLog.hpp"
|
||
|
#include "deRandom.hpp"
|
||
|
#include "deStringUtil.hpp"
|
||
|
#include "deMemory.h"
|
||
|
|
||
|
namespace deqp
|
||
|
{
|
||
|
namespace gles31
|
||
|
{
|
||
|
namespace Functional
|
||
|
{
|
||
|
|
||
|
using std::string;
|
||
|
using std::vector;
|
||
|
using tcu::TestLog;
|
||
|
using namespace glu;
|
||
|
|
||
|
//! Utility for mapping buffers.
|
||
|
class BufferMemMap
|
||
|
{
|
||
|
public:
|
||
|
BufferMemMap (const glw::Functions& gl, deUint32 target, int offset, int size, deUint32 access)
|
||
|
: m_gl (gl)
|
||
|
, m_target (target)
|
||
|
, m_ptr (DE_NULL)
|
||
|
{
|
||
|
m_ptr = gl.mapBufferRange(target, offset, size, access);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
|
||
|
TCU_CHECK(m_ptr);
|
||
|
}
|
||
|
|
||
|
~BufferMemMap (void)
|
||
|
{
|
||
|
m_gl.unmapBuffer(m_target);
|
||
|
}
|
||
|
|
||
|
void* getPtr (void) const { return m_ptr; }
|
||
|
void* operator* (void) const { return m_ptr; }
|
||
|
|
||
|
private:
|
||
|
BufferMemMap (const BufferMemMap& other);
|
||
|
BufferMemMap& operator= (const BufferMemMap& other);
|
||
|
|
||
|
const glw::Functions& m_gl;
|
||
|
const deUint32 m_target;
|
||
|
void* m_ptr;
|
||
|
};
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
|
||
|
class EmptyComputeShaderCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
EmptyComputeShaderCase (Context& context)
|
||
|
: TestCase(context, "empty", "Empty shader")
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = 1) in;\n"
|
||
|
"void main (void) {}\n";
|
||
|
|
||
|
const ShaderProgram program(m_context.getRenderContext(),
|
||
|
ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
gl.dispatchCompute(1, 1, 1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class UBOToSSBOInvertCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
UBOToSSBOInvertCase (Context& context, const char* name, const char* description, int numValues, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_numValues (numValues)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
DE_ASSERT(m_numValues % (m_workSize[0]*m_workSize[1]*m_workSize[2]*m_localSize[0]*m_localSize[1]*m_localSize[2]) == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "uniform Input {\n"
|
||
|
<< " uint values[" << m_numValues << "];\n"
|
||
|
<< "} ub_in;\n"
|
||
|
<< "layout(binding = 1) buffer Output {\n"
|
||
|
<< " uint values[" << m_numValues << "];\n"
|
||
|
<< "} sb_out;\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
|
||
|
<< " uint numValuesPerInv = uint(ub_in.values.length()) / (size.x*size.y*size.z);\n"
|
||
|
<< " uint groupNdx = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
|
||
|
<< " uint offset = numValuesPerInv*groupNdx;\n"
|
||
|
<< "\n"
|
||
|
<< " for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
|
||
|
<< " sb_out.values[offset + ndx] = ~ub_in.values[offset + ndx];\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
const Buffer inputBuffer (m_context.getRenderContext());
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
std::vector<deUint32> inputValues (m_numValues);
|
||
|
|
||
|
// Compute input values.
|
||
|
{
|
||
|
de::Random rnd(0x111223f);
|
||
|
for (int ndx = 0; ndx < (int)inputValues.size(); ndx++)
|
||
|
inputValues[ndx] = rnd.getUint32();
|
||
|
}
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Input buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM_BLOCK, "Input");
|
||
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program.getProgram(), GL_UNIFORM_BLOCK, blockIndex);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "Input.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_UNIFORM, valueIndex);
|
||
|
|
||
|
gl.bindBuffer(GL_UNIFORM_BUFFER, *inputBuffer);
|
||
|
gl.bufferData(GL_UNIFORM_BUFFER, (glw::GLsizeiptr)blockInfo.dataSize, DE_NULL, GL_STATIC_DRAW);
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_UNIFORM_BUFFER, 0, (int)blockInfo.dataSize, GL_MAP_WRITE_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < de::min(valueInfo.arraySize, (deUint32)inputValues.size()); ndx++)
|
||
|
*(deUint32*)((deUint8*)bufMap.getPtr() + valueInfo.offset + ndx*valueInfo.arrayStride) = inputValues[ndx];
|
||
|
}
|
||
|
|
||
|
gl.uniformBlockBinding(program.getProgram(), blockIndex, 0);
|
||
|
gl.bindBufferBase(GL_UNIFORM_BUFFER, 0, *inputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Input buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
for (deUint32 ndx = 0; ndx < valueInfo.arraySize; ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*ndx));
|
||
|
const deUint32 ref = ~inputValues[ndx];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(ndx) + "]");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const int m_numValues;
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class CopyInvertSSBOCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
CopyInvertSSBOCase (Context& context, const char* name, const char* description, int numValues, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_numValues (numValues)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
DE_ASSERT(m_numValues % (m_workSize[0]*m_workSize[1]*m_workSize[2]*m_localSize[0]*m_localSize[1]*m_localSize[2]) == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Input {\n"
|
||
|
<< " uint values[" << m_numValues << "];\n"
|
||
|
<< "} sb_in;\n"
|
||
|
<< "layout (binding = 1) buffer Output {\n"
|
||
|
<< " uint values[" << m_numValues << "];\n"
|
||
|
<< "} sb_out;\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
|
||
|
<< " uint numValuesPerInv = uint(sb_in.values.length()) / (size.x*size.y*size.z);\n"
|
||
|
<< " uint groupNdx = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
|
||
|
<< " uint offset = numValuesPerInv*groupNdx;\n"
|
||
|
<< "\n"
|
||
|
<< " for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
|
||
|
<< " sb_out.values[offset + ndx] = ~sb_in.values[offset + ndx];\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
const Buffer inputBuffer (m_context.getRenderContext());
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
std::vector<deUint32> inputValues (m_numValues);
|
||
|
|
||
|
// Compute input values.
|
||
|
{
|
||
|
de::Random rnd(0x124fef);
|
||
|
for (int ndx = 0; ndx < (int)inputValues.size(); ndx++)
|
||
|
inputValues[ndx] = rnd.getUint32();
|
||
|
}
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Input buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Input");
|
||
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Input.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)blockInfo.dataSize, DE_NULL, GL_STATIC_DRAW);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, (int)blockInfo.dataSize, GL_MAP_WRITE_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
*(deUint32*)((deUint8*)bufMap.getPtr() + valueInfo.offset + ndx*valueInfo.arrayStride) = inputValues[ndx];
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, blockInfo.bufferBinding, *inputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Input buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, blockInfo.bufferBinding, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
for (deUint32 ndx = 0; ndx < valueInfo.arraySize; ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*ndx));
|
||
|
const deUint32 ref = ~inputValues[ndx];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(ndx) + "]");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const int m_numValues;
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class InvertSSBOInPlaceCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
InvertSSBOInPlaceCase (Context& context, const char* name, const char* description, int numValues, bool isSized, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_numValues (numValues)
|
||
|
, m_isSized (isSized)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
DE_ASSERT(m_numValues % (m_workSize[0]*m_workSize[1]*m_workSize[2]*m_localSize[0]*m_localSize[1]*m_localSize[2]) == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer InOut {\n"
|
||
|
<< " uint values[" << (m_isSized ? de::toString(m_numValues) : string("")) << "];\n"
|
||
|
<< "} sb_inout;\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
|
||
|
<< " uint numValuesPerInv = uint(sb_inout.values.length()) / (size.x*size.y*size.z);\n"
|
||
|
<< " uint groupNdx = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
|
||
|
<< " uint offset = numValuesPerInv*groupNdx;\n"
|
||
|
<< "\n"
|
||
|
<< " for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
|
||
|
<< " sb_inout.values[offset + ndx] = ~sb_inout.values[offset + ndx];\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "InOut.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const deUint32 blockSize = valueInfo.arrayStride*(deUint32)m_numValues;
|
||
|
std::vector<deUint32> inputValues (m_numValues);
|
||
|
|
||
|
// Compute input values.
|
||
|
{
|
||
|
de::Random rnd(0x82ce7f);
|
||
|
for (int ndx = 0; ndx < (int)inputValues.size(); ndx++)
|
||
|
inputValues[ndx] = rnd.getUint32();
|
||
|
}
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)(m_isSized ? m_numValues : 0));
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_DRAW);
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, (int)blockSize, GL_MAP_WRITE_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
*(deUint32*)((deUint8*)bufMap.getPtr() + valueInfo.offset + ndx*valueInfo.arrayStride) = inputValues[ndx];
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*ndx));
|
||
|
const deUint32 ref = ~inputValues[ndx];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for InOut.values[") + de::toString(ndx) + "]");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const int m_numValues;
|
||
|
const bool m_isSized;
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class WriteToMultipleSSBOCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
WriteToMultipleSSBOCase (Context& context, const char* name, const char* description, int numValues, bool isSized, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_numValues (numValues)
|
||
|
, m_isSized (isSized)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
DE_ASSERT(m_numValues % (m_workSize[0]*m_workSize[1]*m_workSize[2]*m_localSize[0]*m_localSize[1]*m_localSize[2]) == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Out0 {\n"
|
||
|
<< " uint values[" << (m_isSized ? de::toString(m_numValues) : string("")) << "];\n"
|
||
|
<< "} sb_out0;\n"
|
||
|
<< "layout(binding = 1) buffer Out1 {\n"
|
||
|
<< " uint values[" << (m_isSized ? de::toString(m_numValues) : string("")) << "];\n"
|
||
|
<< "} sb_out1;\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
|
||
|
<< " uint groupNdx = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
|
||
|
<< "\n"
|
||
|
<< " {\n"
|
||
|
<< " uint numValuesPerInv = uint(sb_out0.values.length()) / (size.x*size.y*size.z);\n"
|
||
|
<< " uint offset = numValuesPerInv*groupNdx;\n"
|
||
|
<< "\n"
|
||
|
<< " for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
|
||
|
<< " sb_out0.values[offset + ndx] = offset + ndx;\n"
|
||
|
<< " }\n"
|
||
|
<< " {\n"
|
||
|
<< " uint numValuesPerInv = uint(sb_out1.values.length()) / (size.x*size.y*size.z);\n"
|
||
|
<< " uint offset = numValuesPerInv*groupNdx;\n"
|
||
|
<< "\n"
|
||
|
<< " for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
|
||
|
<< " sb_out1.values[offset + ndx] = uint(sb_out1.values.length()) - offset - ndx;\n"
|
||
|
<< " }\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
const Buffer outputBuffer0 (m_context.getRenderContext());
|
||
|
const deUint32 value0Index = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Out0.values");
|
||
|
const InterfaceVariableInfo value0Info = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, value0Index);
|
||
|
const deUint32 block0Size = value0Info.arrayStride*(deUint32)m_numValues;
|
||
|
|
||
|
const Buffer outputBuffer1 (m_context.getRenderContext());
|
||
|
const deUint32 value1Index = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Out1.values");
|
||
|
const InterfaceVariableInfo value1Info = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, value1Index);
|
||
|
const deUint32 block1Size = value1Info.arrayStride*(deUint32)m_numValues;
|
||
|
|
||
|
TCU_CHECK(value0Info.arraySize == (deUint32)(m_isSized ? m_numValues : 0));
|
||
|
TCU_CHECK(value1Info.arraySize == (deUint32)(m_isSized ? m_numValues : 0));
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer0);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, block0Size, DE_NULL, GL_STREAM_DRAW);
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer0);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
|
||
|
}
|
||
|
{
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer1);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, block1Size, DE_NULL, GL_STREAM_DRAW);
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *outputBuffer1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer0);
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, block0Size, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)m_numValues; ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + value0Info.offset + value0Info.arrayStride*ndx));
|
||
|
const deUint32 ref = ndx;
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Out0.values[") + de::toString(ndx) + "] res=" + de::toString(res) + " ref=" + de::toString(ref));
|
||
|
}
|
||
|
}
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer1);
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, block1Size, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)m_numValues; ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + value1Info.offset + value1Info.arrayStride*ndx));
|
||
|
const deUint32 ref = m_numValues - ndx;
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Out1.values[") + de::toString(ndx) + "] res=" + de::toString(res) + " ref=" + de::toString(ref));
|
||
|
}
|
||
|
}
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const int m_numValues;
|
||
|
const bool m_isSized;
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class SSBOLocalBarrierCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
SSBOLocalBarrierCase (Context& context, const char* name, const char* description, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const int workGroupSize = m_localSize[0]*m_localSize[1]*m_localSize[2];
|
||
|
const int workGroupCount = m_workSize[0]*m_workSize[1]*m_workSize[2];
|
||
|
const int numValues = workGroupSize*workGroupCount;
|
||
|
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Output {\n"
|
||
|
<< " coherent uint values[" << numValues << "];\n"
|
||
|
<< "} sb_out;\n\n"
|
||
|
<< "shared uint offsets[" << workGroupSize << "];\n\n"
|
||
|
<< "void main (void) {\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 localOffs = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_LocalInvocationID.z + gl_WorkGroupSize.x*gl_LocalInvocationID.y + gl_LocalInvocationID.x;\n"
|
||
|
<< "\n"
|
||
|
<< " sb_out.values[globalOffs + localOffs] = globalOffs;\n"
|
||
|
<< " memoryBarrierBuffer();\n"
|
||
|
<< " barrier();\n"
|
||
|
<< " sb_out.values[globalOffs + ((localOffs+1u)%localSize)] += localOffs;\n"
|
||
|
<< " memoryBarrierBuffer();\n"
|
||
|
<< " barrier();\n"
|
||
|
<< " sb_out.values[globalOffs + ((localOffs+2u)%localSize)] += localOffs;\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (int groupNdx = 0; groupNdx < workGroupCount; groupNdx++)
|
||
|
{
|
||
|
for (int localOffs = 0; localOffs < workGroupSize; localOffs++)
|
||
|
{
|
||
|
const int globalOffs = groupNdx*workGroupSize;
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*(globalOffs + localOffs)));
|
||
|
const int offs0 = localOffs-1 < 0 ? ((localOffs+workGroupSize-1)%workGroupSize) : ((localOffs-1)%workGroupSize);
|
||
|
const int offs1 = localOffs-2 < 0 ? ((localOffs+workGroupSize-2)%workGroupSize) : ((localOffs-2)%workGroupSize);
|
||
|
const deUint32 ref = (deUint32)(globalOffs + offs0 + offs1);
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(globalOffs + localOffs) + "]");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class SSBOBarrierCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
SSBOBarrierCase (Context& context, const char* name, const char* description, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
const char* const glslVersionDeclaration = getGLSLVersionDeclaration(glslVersion);
|
||
|
|
||
|
std::ostringstream src0;
|
||
|
src0 << glslVersionDeclaration << "\n"
|
||
|
<< "layout (local_size_x = 1) in;\n"
|
||
|
"uniform uint u_baseVal;\n"
|
||
|
"layout(binding = 1) buffer Output {\n"
|
||
|
" uint values[];\n"
|
||
|
"};\n"
|
||
|
"void main (void) {\n"
|
||
|
" uint offset = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
|
||
|
" values[offset] = u_baseVal+offset;\n"
|
||
|
"}\n";
|
||
|
|
||
|
std::ostringstream src1;
|
||
|
src1 << glslVersionDeclaration << "\n"
|
||
|
<< "layout (local_size_x = 1) in;\n"
|
||
|
"uniform uint u_baseVal;\n"
|
||
|
"layout(binding = 1) buffer Input {\n"
|
||
|
" uint values[];\n"
|
||
|
"};\n"
|
||
|
"layout(binding = 0) buffer Output {\n"
|
||
|
" coherent uint sum;\n"
|
||
|
"};\n"
|
||
|
"void main (void) {\n"
|
||
|
" uint offset = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
|
||
|
" uint value = values[offset];\n"
|
||
|
" atomicAdd(sum, value);\n"
|
||
|
"}\n";
|
||
|
|
||
|
const ShaderProgram program0 (m_context.getRenderContext(), ProgramSources() << ComputeSource(src0.str()));
|
||
|
const ShaderProgram program1 (m_context.getRenderContext(), ProgramSources() << ComputeSource(src1.str()));
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer tempBuffer (m_context.getRenderContext());
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const deUint32 baseValue = 127;
|
||
|
|
||
|
m_testCtx.getLog() << program0 << program1;
|
||
|
if (!program0.isOk() || !program1.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
// Temp buffer setup
|
||
|
{
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program0.getProgram(), GL_BUFFER_VARIABLE, "values[0]");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program0.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const deUint32 bufferSize = valueInfo.arrayStride*m_workSize[0]*m_workSize[1]*m_workSize[2];
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *tempBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)bufferSize, DE_NULL, GL_STATIC_DRAW);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *tempBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Temp buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program1.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_WRITE_BIT);
|
||
|
deMemset(bufMap.getPtr(), 0, blockSize);
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.useProgram(program0.getProgram());
|
||
|
gl.uniform1ui(gl.getUniformLocation(program0.getProgram(), "u_baseVal"), baseValue);
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
gl.memoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||
|
gl.useProgram(program1.getProgram());
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to dispatch commands");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program1.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_BUFFER_VARIABLE, "sum");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program1.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset));
|
||
|
deUint32 ref = 0;
|
||
|
|
||
|
for (int ndx = 0; ndx < m_workSize[0]*m_workSize[1]*m_workSize[2]; ndx++)
|
||
|
ref += baseValue + (deUint32)ndx;
|
||
|
|
||
|
if (res != ref)
|
||
|
{
|
||
|
m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed, expected " << ref << ", got " << res << TestLog::EndMessage;
|
||
|
throw tcu::TestError("Comparison failed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class BasicSharedVarCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
BasicSharedVarCase (Context& context, const char* name, const char* description, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const int workGroupSize = m_localSize[0]*m_localSize[1]*m_localSize[2];
|
||
|
const int workGroupCount = m_workSize[0]*m_workSize[1]*m_workSize[2];
|
||
|
const int numValues = workGroupSize*workGroupCount;
|
||
|
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Output {\n"
|
||
|
<< " uint values[" << numValues << "];\n"
|
||
|
<< "} sb_out;\n\n"
|
||
|
<< "shared uint offsets[" << workGroupSize << "];\n\n"
|
||
|
<< "void main (void) {\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 localOffs = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_LocalInvocationID.z + gl_WorkGroupSize.x*gl_LocalInvocationID.y + gl_LocalInvocationID.x;\n"
|
||
|
<< "\n"
|
||
|
<< " offsets[localSize-localOffs-1u] = globalOffs + localOffs*localOffs;\n"
|
||
|
<< " barrier();\n"
|
||
|
<< " sb_out.values[globalOffs + localOffs] = offsets[localOffs];\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (int groupNdx = 0; groupNdx < workGroupCount; groupNdx++)
|
||
|
{
|
||
|
for (int localOffs = 0; localOffs < workGroupSize; localOffs++)
|
||
|
{
|
||
|
const int globalOffs = groupNdx*workGroupSize;
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*(globalOffs + localOffs)));
|
||
|
const deUint32 ref = (deUint32)(globalOffs + (workGroupSize-localOffs-1)*(workGroupSize-localOffs-1));
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(globalOffs + localOffs) + "]");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class SharedVarAtomicOpCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
SharedVarAtomicOpCase (Context& context, const char* name, const char* description, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const int workGroupSize = m_localSize[0]*m_localSize[1]*m_localSize[2];
|
||
|
const int workGroupCount = m_workSize[0]*m_workSize[1]*m_workSize[2];
|
||
|
const int numValues = workGroupSize*workGroupCount;
|
||
|
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Output {\n"
|
||
|
<< " uint values[" << numValues << "];\n"
|
||
|
<< "} sb_out;\n\n"
|
||
|
<< "shared uint count;\n\n"
|
||
|
<< "void main (void) {\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"
|
||
|
<< "\n"
|
||
|
<< " count = 0u;\n"
|
||
|
<< " barrier();\n"
|
||
|
<< " uint oldVal = atomicAdd(count, 1u);\n"
|
||
|
<< " sb_out.values[globalOffs+oldVal] = oldVal+1u;\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
for (int groupNdx = 0; groupNdx < workGroupCount; groupNdx++)
|
||
|
{
|
||
|
for (int localOffs = 0; localOffs < workGroupSize; localOffs++)
|
||
|
{
|
||
|
const int globalOffs = groupNdx*workGroupSize;
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*(globalOffs + localOffs)));
|
||
|
const deUint32 ref = (deUint32)(localOffs+1);
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(globalOffs + localOffs) + "]");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
class CopyImageToSSBOCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
CopyImageToSSBOCase (Context& context, const char* name, const char* description, const tcu::IVec2& localSize, const tcu::IVec2& imageSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_imageSize (imageSize)
|
||
|
{
|
||
|
DE_ASSERT(m_imageSize[0] % m_localSize[0] == 0);
|
||
|
DE_ASSERT(m_imageSize[1] % m_localSize[1] == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ") in;\n"
|
||
|
<< "layout(r32ui, binding = 1) readonly uniform highp uimage2D u_srcImg;\n"
|
||
|
<< "layout(binding = 0) buffer Output {\n"
|
||
|
<< " uint values[" << (m_imageSize[0]*m_imageSize[1]) << "];\n"
|
||
|
<< "} sb_out;\n\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uint stride = gl_NumWorkGroups.x*gl_WorkGroupSize.x;\n"
|
||
|
<< " uint value = imageLoad(u_srcImg, ivec2(gl_GlobalInvocationID.xy)).x;\n"
|
||
|
<< " sb_out.values[gl_GlobalInvocationID.y*stride + gl_GlobalInvocationID.x] = value;\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const Texture inputTexture (m_context.getRenderContext());
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
const tcu::IVec2 workSize = m_imageSize / m_localSize;
|
||
|
de::Random rnd (0xab2c7);
|
||
|
vector<deUint32> inputValues (m_imageSize[0]*m_imageSize[1]);
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Input values
|
||
|
for (vector<deUint32>::iterator i = inputValues.begin(); i != inputValues.end(); ++i)
|
||
|
*i = rnd.getUint32();
|
||
|
|
||
|
// Input image setup
|
||
|
gl.bindTexture(GL_TEXTURE_2D, *inputTexture);
|
||
|
gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_imageSize[0], m_imageSize[1]);
|
||
|
gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_imageSize[0], m_imageSize[1], GL_RED_INTEGER, GL_UNSIGNED_INT, &inputValues[0]);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");
|
||
|
|
||
|
// Bind to unit 1
|
||
|
gl.bindImageTexture(1, *inputTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Image setup failed");
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(workSize[0], workSize[1], 1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < valueInfo.arraySize; ndx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*ndx));
|
||
|
const deUint32 ref = inputValues[ndx];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(ndx) + "]");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec2 m_localSize;
|
||
|
const tcu::IVec2 m_imageSize;
|
||
|
};
|
||
|
|
||
|
class CopySSBOToImageCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
CopySSBOToImageCase (Context& context, const char* name, const char* description, const tcu::IVec2& localSize, const tcu::IVec2& imageSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_imageSize (imageSize)
|
||
|
{
|
||
|
DE_ASSERT(m_imageSize[0] % m_localSize[0] == 0);
|
||
|
DE_ASSERT(m_imageSize[1] % m_localSize[1] == 0);
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ") in;\n"
|
||
|
<< "layout(r32ui, binding = 1) writeonly uniform highp uimage2D u_dstImg;\n"
|
||
|
<< "buffer Input {\n"
|
||
|
<< " uint values[" << (m_imageSize[0]*m_imageSize[1]) << "];\n"
|
||
|
<< "} sb_in;\n\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uint stride = gl_NumWorkGroups.x*gl_WorkGroupSize.x;\n"
|
||
|
<< " uint value = sb_in.values[gl_GlobalInvocationID.y*stride + gl_GlobalInvocationID.x];\n"
|
||
|
<< " imageStore(u_dstImg, ivec2(gl_GlobalInvocationID.xy), uvec4(value, 0, 0, 0));\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer inputBuffer (m_context.getRenderContext());
|
||
|
const Texture outputTexture (m_context.getRenderContext());
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
const tcu::IVec2 workSize = m_imageSize / m_localSize;
|
||
|
de::Random rnd (0x77238ac2);
|
||
|
vector<deUint32> inputValues (m_imageSize[0]*m_imageSize[1]);
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Input values
|
||
|
for (vector<deUint32>::iterator i = inputValues.begin(); i != inputValues.end(); ++i)
|
||
|
*i = rnd.getUint32();
|
||
|
|
||
|
// Input buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Input");
|
||
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Input.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)blockInfo.dataSize, DE_NULL, GL_STATIC_DRAW);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, (int)blockInfo.dataSize, GL_MAP_WRITE_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
*(deUint32*)((deUint8*)bufMap.getPtr() + valueInfo.offset + ndx*valueInfo.arrayStride) = inputValues[ndx];
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, blockInfo.bufferBinding, *inputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Input buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output image setup
|
||
|
gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
|
||
|
gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_imageSize[0], m_imageSize[1]);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");
|
||
|
|
||
|
// Bind to unit 1
|
||
|
gl.bindImageTexture(1, *outputTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Image setup failed");
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(workSize[0], workSize[1], 1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
Framebuffer fbo (m_context.getRenderContext());
|
||
|
vector<deUint32> pixels (inputValues.size()*4);
|
||
|
|
||
|
gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
|
||
|
gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *outputTexture, 0);
|
||
|
TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||
|
|
||
|
// \note In ES3 we have to use GL_RGBA_INTEGER
|
||
|
gl.readBuffer(GL_COLOR_ATTACHMENT0);
|
||
|
gl.readPixels(0, 0, m_imageSize[0], m_imageSize[1], GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
{
|
||
|
const deUint32 res = pixels[ndx*4];
|
||
|
const deUint32 ref = inputValues[ndx];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for pixel ") + de::toString(ndx));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec2 m_localSize;
|
||
|
const tcu::IVec2 m_imageSize;
|
||
|
};
|
||
|
|
||
|
class ImageAtomicOpCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
ImageAtomicOpCase (Context& context, const char* name, const char* description, int localSize, const tcu::IVec2& imageSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_imageSize (imageSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void init (void)
|
||
|
{
|
||
|
auto contextType = m_context.getRenderContext().getType();
|
||
|
if (!glu::contextSupports(contextType, glu::ApiType::es(3, 2)) &&
|
||
|
!glu::contextSupports(contextType, glu::ApiType::core(4, 5)) &&
|
||
|
!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic"))
|
||
|
TCU_THROW(NotSupportedError, "Test requires OES_shader_image_atomic extension");
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
glu::ContextType contextType = m_context.getRenderContext().getType();
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType);
|
||
|
const bool supportsES32orGL45 = glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
|
||
|
glu::contextSupports(contextType, glu::ApiType::core(4, 5));
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< (supportsES32orGL45 ? "\n" : "#extension GL_OES_shader_image_atomic : require\n")
|
||
|
<< "layout (local_size_x = " << m_localSize << ") in;\n"
|
||
|
<< "layout(r32ui, binding = 1) uniform highp uimage2D u_dstImg;\n"
|
||
|
<< "buffer Input {\n"
|
||
|
<< " uint values[" << (m_imageSize[0]*m_imageSize[1]*m_localSize) << "];\n"
|
||
|
<< "} sb_in;\n\n"
|
||
|
<< "void main (void) {\n"
|
||
|
<< " uint stride = gl_NumWorkGroups.x*gl_WorkGroupSize.x;\n"
|
||
|
<< " uint value = sb_in.values[gl_GlobalInvocationID.y*stride + gl_GlobalInvocationID.x];\n"
|
||
|
<< "\n"
|
||
|
<< " if (gl_LocalInvocationIndex == 0u)\n"
|
||
|
<< " imageStore(u_dstImg, ivec2(gl_WorkGroupID.xy), uvec4(0));\n"
|
||
|
<< " barrier();\n"
|
||
|
<< " imageAtomicAdd(u_dstImg, ivec2(gl_WorkGroupID.xy), value);\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer inputBuffer (m_context.getRenderContext());
|
||
|
const Texture outputTexture (m_context.getRenderContext());
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ShaderSource(SHADERTYPE_COMPUTE, src.str()));
|
||
|
de::Random rnd (0x77238ac2);
|
||
|
vector<deUint32> inputValues (m_imageSize[0]*m_imageSize[1]*m_localSize);
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_imageSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Input values
|
||
|
for (vector<deUint32>::iterator i = inputValues.begin(); i != inputValues.end(); ++i)
|
||
|
*i = rnd.getUint32();
|
||
|
|
||
|
// Input buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Input");
|
||
|
const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Input.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)blockInfo.dataSize, DE_NULL, GL_STATIC_DRAW);
|
||
|
|
||
|
TCU_CHECK(valueInfo.arraySize == (deUint32)inputValues.size());
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, (int)blockInfo.dataSize, GL_MAP_WRITE_BIT);
|
||
|
|
||
|
for (deUint32 ndx = 0; ndx < (deUint32)inputValues.size(); ndx++)
|
||
|
*(deUint32*)((deUint8*)bufMap.getPtr() + valueInfo.offset + ndx*valueInfo.arrayStride) = inputValues[ndx];
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, blockInfo.bufferBinding, *inputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Input buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output image setup
|
||
|
gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
|
||
|
gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_imageSize[0], m_imageSize[1]);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");
|
||
|
|
||
|
// Bind to unit 1
|
||
|
gl.bindImageTexture(1, *outputTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Image setup failed");
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_imageSize[0], m_imageSize[1], 1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
Framebuffer fbo (m_context.getRenderContext());
|
||
|
vector<deUint32> pixels (m_imageSize[0]*m_imageSize[1]*4);
|
||
|
|
||
|
gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
|
||
|
gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *outputTexture, 0);
|
||
|
TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||
|
|
||
|
// \note In ES3 we have to use GL_RGBA_INTEGER
|
||
|
gl.readBuffer(GL_COLOR_ATTACHMENT0);
|
||
|
gl.readPixels(0, 0, m_imageSize[0], m_imageSize[1], GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
|
||
|
|
||
|
for (int pixelNdx = 0; pixelNdx < (int)inputValues.size()/m_localSize; pixelNdx++)
|
||
|
{
|
||
|
const deUint32 res = pixels[pixelNdx*4];
|
||
|
deUint32 ref = 0;
|
||
|
|
||
|
for (int offs = 0; offs < m_localSize; offs++)
|
||
|
ref += inputValues[pixelNdx*m_localSize + offs];
|
||
|
|
||
|
if (res != ref)
|
||
|
throw tcu::TestError(string("Comparison failed for pixel ") + de::toString(pixelNdx));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const int m_localSize;
|
||
|
const tcu::IVec2 m_imageSize;
|
||
|
};
|
||
|
|
||
|
class ImageBarrierCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
ImageBarrierCase (Context& context, const char* name, const char* description, const tcu::IVec2& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
const char* const glslVersionDeclaration = getGLSLVersionDeclaration(glslVersion);
|
||
|
|
||
|
std::ostringstream src0;
|
||
|
src0 << glslVersionDeclaration << "\n"
|
||
|
<< "layout (local_size_x = 1) in;\n"
|
||
|
"uniform uint u_baseVal;\n"
|
||
|
"layout(r32ui, binding = 2) writeonly uniform highp uimage2D u_img;\n"
|
||
|
"void main (void) {\n"
|
||
|
" uint offset = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
|
||
|
" imageStore(u_img, ivec2(gl_WorkGroupID.xy), uvec4(offset+u_baseVal, 0, 0, 0));\n"
|
||
|
"}\n";
|
||
|
|
||
|
std::ostringstream src1;
|
||
|
src1 << glslVersionDeclaration << "\n"
|
||
|
<< "layout (local_size_x = 1) in;\n"
|
||
|
"layout(r32ui, binding = 2) readonly uniform highp uimage2D u_img;\n"
|
||
|
"layout(binding = 0) buffer Output {\n"
|
||
|
" coherent uint sum;\n"
|
||
|
"};\n"
|
||
|
"void main (void) {\n"
|
||
|
" uint value = imageLoad(u_img, ivec2(gl_WorkGroupID.xy)).x;\n"
|
||
|
" atomicAdd(sum, value);\n"
|
||
|
"}\n";
|
||
|
|
||
|
const ShaderProgram program0 (m_context.getRenderContext(), ProgramSources() << ComputeSource(src0.str()));
|
||
|
const ShaderProgram program1 (m_context.getRenderContext(), ProgramSources() << ComputeSource(src1.str()));
|
||
|
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Texture tempTexture (m_context.getRenderContext());
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const deUint32 baseValue = 127;
|
||
|
|
||
|
m_testCtx.getLog() << program0 << program1;
|
||
|
if (!program0.isOk() || !program1.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
// Temp texture setup
|
||
|
gl.bindTexture(GL_TEXTURE_2D, *tempTexture);
|
||
|
gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_workSize[0], m_workSize[1]);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");
|
||
|
|
||
|
// Bind to unit 2
|
||
|
gl.bindImageTexture(2, *tempTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Image setup failed");
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program1.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
|
||
|
{
|
||
|
const BufferMemMap bufMap(gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_WRITE_BIT);
|
||
|
deMemset(bufMap.getPtr(), 0, blockSize);
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.useProgram(program0.getProgram());
|
||
|
gl.uniform1ui(gl.getUniformLocation(program0.getProgram(), "u_baseVal"), baseValue);
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], 1);
|
||
|
gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
||
|
gl.useProgram(program1.getProgram());
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], 1);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to dispatch commands");
|
||
|
|
||
|
// Read back and compare
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program1.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program1.getProgram(), GL_BUFFER_VARIABLE, "sum");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program1.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset));
|
||
|
deUint32 ref = 0;
|
||
|
|
||
|
for (int ndx = 0; ndx < m_workSize[0]*m_workSize[1]; ndx++)
|
||
|
ref += baseValue + (deUint32)ndx;
|
||
|
|
||
|
if (res != ref)
|
||
|
{
|
||
|
m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed, expected " << ref << ", got " << res << TestLog::EndMessage;
|
||
|
throw tcu::TestError("Comparison failed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec2 m_workSize;
|
||
|
};
|
||
|
|
||
|
class AtomicCounterCase : public TestCase
|
||
|
{
|
||
|
public:
|
||
|
AtomicCounterCase (Context& context, const char* name, const char* description, const tcu::IVec3& localSize, const tcu::IVec3& workSize)
|
||
|
: TestCase (context, name, description)
|
||
|
, m_localSize (localSize)
|
||
|
, m_workSize (workSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
IterateResult iterate (void)
|
||
|
{
|
||
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
||
|
const Buffer outputBuffer (m_context.getRenderContext());
|
||
|
const Buffer counterBuffer (m_context.getRenderContext());
|
||
|
const int workGroupSize = m_localSize[0]*m_localSize[1]*m_localSize[2];
|
||
|
const int workGroupCount = m_workSize[0]*m_workSize[1]*m_workSize[2];
|
||
|
const int numValues = workGroupSize*workGroupCount;
|
||
|
|
||
|
const GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
|
||
|
std::ostringstream src;
|
||
|
|
||
|
src << getGLSLVersionDeclaration(glslVersion) << "\n"
|
||
|
<< "layout (local_size_x = " << m_localSize[0] << ", local_size_y = " << m_localSize[1] << ", local_size_z = " << m_localSize[2] << ") in;\n"
|
||
|
<< "layout(binding = 0) buffer Output {\n"
|
||
|
<< " uint values[" << numValues << "];\n"
|
||
|
<< "} sb_out;\n\n"
|
||
|
<< "layout(binding = 0, offset = 0) uniform atomic_uint u_count;\n\n"
|
||
|
<< "void main (void) {\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 localOffs = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_LocalInvocationID.z + gl_WorkGroupSize.x*gl_LocalInvocationID.y + gl_LocalInvocationID.x;\n"
|
||
|
<< "\n"
|
||
|
<< " uint oldVal = atomicCounterIncrement(u_count);\n"
|
||
|
<< " sb_out.values[globalOffs+localOffs] = oldVal;\n"
|
||
|
<< "}\n";
|
||
|
|
||
|
const ShaderProgram program (m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
|
||
|
|
||
|
m_testCtx.getLog() << program;
|
||
|
if (!program.isOk())
|
||
|
TCU_FAIL("Compile failed");
|
||
|
|
||
|
m_testCtx.getLog() << TestLog::Message << "Work groups: " << m_workSize << TestLog::EndMessage;
|
||
|
|
||
|
gl.useProgram(program.getProgram());
|
||
|
|
||
|
// Atomic counter buffer setup
|
||
|
{
|
||
|
const deUint32 uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_count");
|
||
|
const deUint32 bufferIndex = getProgramResourceUint(gl, program.getProgram(), GL_UNIFORM, uniformIndex, GL_ATOMIC_COUNTER_BUFFER_INDEX);
|
||
|
const deUint32 bufferSize = getProgramResourceUint(gl, program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, *counterBuffer);
|
||
|
gl.bufferData(GL_ATOMIC_COUNTER_BUFFER, bufferSize, DE_NULL, GL_STREAM_READ);
|
||
|
|
||
|
{
|
||
|
const BufferMemMap memMap(gl, GL_ATOMIC_COUNTER_BUFFER, 0, bufferSize, GL_MAP_WRITE_BIT);
|
||
|
deMemset(memMap.getPtr(), 0, (int)bufferSize);
|
||
|
}
|
||
|
|
||
|
gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, *counterBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Atomic counter buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Output buffer setup
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
|
||
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
|
||
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
|
||
|
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
|
||
|
}
|
||
|
|
||
|
// Dispatch compute workload
|
||
|
gl.dispatchCompute(m_workSize[0], m_workSize[1], m_workSize[2]);
|
||
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
|
||
|
|
||
|
// Read back and compare atomic counter
|
||
|
{
|
||
|
const deUint32 uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_count");
|
||
|
const deUint32 uniformOffset = getProgramResourceUint(gl, program.getProgram(), GL_UNIFORM, uniformIndex, GL_OFFSET);
|
||
|
const deUint32 bufferIndex = getProgramResourceUint(gl, program.getProgram(), GL_UNIFORM, uniformIndex, GL_ATOMIC_COUNTER_BUFFER_INDEX);
|
||
|
const deUint32 bufferSize = getProgramResourceUint(gl, program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const BufferMemMap bufMap (gl, GL_ATOMIC_COUNTER_BUFFER, 0, bufferSize, GL_MAP_READ_BIT);
|
||
|
|
||
|
const deUint32 resVal = *((const deUint32*)((const deUint8*)bufMap.getPtr() + uniformOffset));
|
||
|
|
||
|
if (resVal != (deUint32)numValues)
|
||
|
throw tcu::TestError("Invalid atomic counter value");
|
||
|
}
|
||
|
|
||
|
// Read back and compare SSBO
|
||
|
{
|
||
|
const deUint32 blockIndex = gl.getProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
|
||
|
const int blockSize = getProgramResourceInt(gl, program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
|
||
|
const deUint32 valueIndex = gl.getProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.values");
|
||
|
const InterfaceVariableInfo valueInfo = getProgramInterfaceVariableInfo(gl, program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
|
||
|
const BufferMemMap bufMap (gl, GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
|
||
|
deUint32 valSum = 0;
|
||
|
deUint32 refSum = 0;
|
||
|
|
||
|
for (int valNdx = 0; valNdx < numValues; valNdx++)
|
||
|
{
|
||
|
const deUint32 res = *((const deUint32*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*valNdx));
|
||
|
|
||
|
valSum += res;
|
||
|
refSum += (deUint32)valNdx;
|
||
|
|
||
|
if (!de::inBounds<deUint32>(res, 0, (deUint32)numValues))
|
||
|
throw tcu::TestError(string("Comparison failed for Output.values[") + de::toString(valNdx) + "]");
|
||
|
}
|
||
|
|
||
|
if (valSum != refSum)
|
||
|
throw tcu::TestError("Total sum of values in Output.values doesn't match");
|
||
|
}
|
||
|
|
||
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
||
|
return STOP;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const tcu::IVec3 m_localSize;
|
||
|
const tcu::IVec3 m_workSize;
|
||
|
};
|
||
|
|
||
|
} // anonymous
|
||
|
|
||
|
BasicComputeShaderTests::BasicComputeShaderTests (Context& context)
|
||
|
: TestCaseGroup(context, "basic", "Basic Compute Shader Tests")
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicComputeShaderTests::~BasicComputeShaderTests (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void BasicComputeShaderTests::init (void)
|
||
|
{
|
||
|
addChild(new EmptyComputeShaderCase(m_context));
|
||
|
|
||
|
addChild(new UBOToSSBOInvertCase (m_context, "ubo_to_ssbo_single_invocation", "Copy from UBO to SSBO, inverting bits", 256, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new UBOToSSBOInvertCase (m_context, "ubo_to_ssbo_single_group", "Copy from UBO to SSBO, inverting bits", 1024, tcu::IVec3(2,1,4), tcu::IVec3(1,1,1)));
|
||
|
addChild(new UBOToSSBOInvertCase (m_context, "ubo_to_ssbo_multiple_invocations", "Copy from UBO to SSBO, inverting bits", 1024, tcu::IVec3(1,1,1), tcu::IVec3(2,4,1)));
|
||
|
addChild(new UBOToSSBOInvertCase (m_context, "ubo_to_ssbo_multiple_groups", "Copy from UBO to SSBO, inverting bits", 1024, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new CopyInvertSSBOCase (m_context, "copy_ssbo_single_invocation", "Copy between SSBOs, inverting bits", 256, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new CopyInvertSSBOCase (m_context, "copy_ssbo_multiple_invocations", "Copy between SSBOs, inverting bits", 1024, tcu::IVec3(1,1,1), tcu::IVec3(2,4,1)));
|
||
|
addChild(new CopyInvertSSBOCase (m_context, "copy_ssbo_multiple_groups", "Copy between SSBOs, inverting bits", 1024, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new InvertSSBOInPlaceCase (m_context, "ssbo_rw_single_invocation", "Read and write same SSBO", 256, true, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new InvertSSBOInPlaceCase (m_context, "ssbo_rw_multiple_groups", "Read and write same SSBO", 1024, true, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new InvertSSBOInPlaceCase (m_context, "ssbo_unsized_arr_single_invocation", "Read and write same SSBO", 256, false, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new InvertSSBOInPlaceCase (m_context, "ssbo_unsized_arr_multiple_groups", "Read and write same SSBO", 1024, false, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new WriteToMultipleSSBOCase(m_context, "write_multiple_arr_single_invocation", "Write to multiple SSBOs", 256, true, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new WriteToMultipleSSBOCase(m_context, "write_multiple_arr_multiple_groups", "Write to multiple SSBOs", 1024, true, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new WriteToMultipleSSBOCase(m_context, "write_multiple_unsized_arr_single_invocation", "Write to multiple SSBOs", 256, false, tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new WriteToMultipleSSBOCase(m_context, "write_multiple_unsized_arr_multiple_groups", "Write to multiple SSBOs", 1024, false, tcu::IVec3(1,4,2), tcu::IVec3(2,2,4)));
|
||
|
|
||
|
addChild(new SSBOLocalBarrierCase (m_context, "ssbo_local_barrier_single_invocation", "SSBO local barrier usage", tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new SSBOLocalBarrierCase (m_context, "ssbo_local_barrier_single_group", "SSBO local barrier usage", tcu::IVec3(3,2,5), tcu::IVec3(1,1,1)));
|
||
|
addChild(new SSBOLocalBarrierCase (m_context, "ssbo_local_barrier_multiple_groups", "SSBO local barrier usage", tcu::IVec3(3,4,1), tcu::IVec3(2,7,3)));
|
||
|
|
||
|
addChild(new SSBOBarrierCase (m_context, "ssbo_cmd_barrier_single", "SSBO memory barrier usage", tcu::IVec3(1,1,1)));
|
||
|
addChild(new SSBOBarrierCase (m_context, "ssbo_cmd_barrier_multiple", "SSBO memory barrier usage", tcu::IVec3(11,5,7)));
|
||
|
|
||
|
addChild(new BasicSharedVarCase (m_context, "shared_var_single_invocation", "Basic shared variable usage", tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new BasicSharedVarCase (m_context, "shared_var_single_group", "Basic shared variable usage", tcu::IVec3(3,2,5), tcu::IVec3(1,1,1)));
|
||
|
addChild(new BasicSharedVarCase (m_context, "shared_var_multiple_invocations", "Basic shared variable usage", tcu::IVec3(1,1,1), tcu::IVec3(2,5,4)));
|
||
|
addChild(new BasicSharedVarCase (m_context, "shared_var_multiple_groups", "Basic shared variable usage", tcu::IVec3(3,4,1), tcu::IVec3(2,7,3)));
|
||
|
|
||
|
addChild(new SharedVarAtomicOpCase (m_context, "shared_atomic_op_single_invocation", "Atomic operation with shared var", tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new SharedVarAtomicOpCase (m_context, "shared_atomic_op_single_group", "Atomic operation with shared var", tcu::IVec3(3,2,5), tcu::IVec3(1,1,1)));
|
||
|
addChild(new SharedVarAtomicOpCase (m_context, "shared_atomic_op_multiple_invocations", "Atomic operation with shared var", tcu::IVec3(1,1,1), tcu::IVec3(2,5,4)));
|
||
|
addChild(new SharedVarAtomicOpCase (m_context, "shared_atomic_op_multiple_groups", "Atomic operation with shared var", tcu::IVec3(3,4,1), tcu::IVec3(2,7,3)));
|
||
|
|
||
|
addChild(new CopyImageToSSBOCase (m_context, "copy_image_to_ssbo_small", "Image to SSBO copy", tcu::IVec2(1,1), tcu::IVec2(64,64)));
|
||
|
addChild(new CopyImageToSSBOCase (m_context, "copy_image_to_ssbo_large", "Image to SSBO copy", tcu::IVec2(2,4), tcu::IVec2(512,512)));
|
||
|
|
||
|
addChild(new CopySSBOToImageCase (m_context, "copy_ssbo_to_image_small", "SSBO to image copy", tcu::IVec2(1,1), tcu::IVec2(64,64)));
|
||
|
addChild(new CopySSBOToImageCase (m_context, "copy_ssbo_to_image_large", "SSBO to image copy", tcu::IVec2(2,4), tcu::IVec2(512,512)));
|
||
|
|
||
|
addChild(new ImageAtomicOpCase (m_context, "image_atomic_op_local_size_1", "Atomic operation with image", 1, tcu::IVec2(64,64)));
|
||
|
addChild(new ImageAtomicOpCase (m_context, "image_atomic_op_local_size_8", "Atomic operation with image", 8, tcu::IVec2(64,64)));
|
||
|
|
||
|
addChild(new ImageBarrierCase (m_context, "image_barrier_single", "Image barrier", tcu::IVec2(1,1)));
|
||
|
addChild(new ImageBarrierCase (m_context, "image_barrier_multiple", "Image barrier", tcu::IVec2(64,64)));
|
||
|
|
||
|
addChild(new AtomicCounterCase (m_context, "atomic_counter_single_invocation", "Basic atomic counter test", tcu::IVec3(1,1,1), tcu::IVec3(1,1,1)));
|
||
|
addChild(new AtomicCounterCase (m_context, "atomic_counter_single_group", "Basic atomic counter test", tcu::IVec3(3,2,5), tcu::IVec3(1,1,1)));
|
||
|
addChild(new AtomicCounterCase (m_context, "atomic_counter_multiple_invocations", "Basic atomic counter test", tcu::IVec3(1,1,1), tcu::IVec3(2,5,4)));
|
||
|
addChild(new AtomicCounterCase (m_context, "atomic_counter_multiple_groups", "Basic atomic counter test", tcu::IVec3(3,4,1), tcu::IVec3(2,7,3)));
|
||
|
}
|
||
|
|
||
|
} // Functional
|
||
|
} // gles31
|
||
|
} // deqp
|