blob: f6452420850efdf4f1ef5dde38736834316b8a54 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
#include "gl4cComputeShaderTests.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuMatrix.hpp"
#include "tcuMatrixUtil.hpp"
#include "tcuRenderTarget.hpp"
#include <cmath>
#include <cstdarg>
#include <sstream>
namespace gl4cts
{
using namespace glw;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::UVec4;
using tcu::UVec3;
using tcu::Mat4;
namespace
{
typedef Vec3 vec2;
typedef Vec3 vec3;
typedef Vec4 vec4;
typedef UVec3 uvec3;
typedef UVec4 uvec4;
typedef Mat4 mat4;
const char* const kGLSLVer = "#version 430 core\n";
class ComputeShaderBase : public deqp::SubcaseBase
{
public:
virtual ~ComputeShaderBase()
{
}
ComputeShaderBase()
: renderTarget(m_context.getRenderContext().getRenderTarget()), pixelFormat(renderTarget.getPixelFormat())
{
float epsilon_zero = 1.f / (1 << 13);
if (pixelFormat.redBits != 0 && pixelFormat.greenBits != 0 && pixelFormat.blueBits != 0 &&
pixelFormat.alphaBits != 0)
{
g_color_eps = vec4(1.f / ((float)(1 << pixelFormat.redBits) - 1.0f),
1.f / ((float)(1 << pixelFormat.greenBits) - 1.0f),
1.f / ((float)(1 << pixelFormat.blueBits) - 1.0f),
1.f / ((float)(1 << pixelFormat.alphaBits) - 1.0f)) +
vec4(epsilon_zero);
}
else if (pixelFormat.redBits != 0 && pixelFormat.greenBits != 0 && pixelFormat.blueBits != 0)
{
g_color_eps = vec4(1.f / ((float)(1 << pixelFormat.redBits) - 1.0f),
1.f / ((float)(1 << pixelFormat.greenBits) - 1.0f),
1.f / ((float)(1 << pixelFormat.blueBits) - 1.0f), 1.f) +
vec4(epsilon_zero);
}
else
{
g_color_eps = vec4(epsilon_zero);
}
}
const tcu::RenderTarget& renderTarget;
const tcu::PixelFormat& pixelFormat;
vec4 g_color_eps;
uvec3 IndexTo3DCoord(GLuint idx, GLuint max_x, GLuint max_y)
{
const GLuint x = idx % max_x;
idx /= max_x;
const GLuint y = idx % max_y;
idx /= max_y;
const GLuint z = idx;
return uvec3(x, y, z);
}
bool CheckProgram(GLuint program, bool* compile_error = NULL)
{
GLint compile_status = GL_TRUE;
GLint status = GL_TRUE;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint attached_shaders;
glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
if (attached_shaders > 0)
{
std::vector<GLuint> shaders(attached_shaders);
glGetAttachedShaders(program, attached_shaders, NULL, &shaders[0]);
for (GLint i = 0; i < attached_shaders; ++i)
{
GLenum type;
glGetShaderiv(shaders[i], GL_SHADER_TYPE, reinterpret_cast<GLint*>(&type));
switch (type)
{
case GL_VERTEX_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Vertex Shader ***" << tcu::TestLog::EndMessage;
break;
case GL_TESS_CONTROL_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Tessellation Control Shader ***"
<< tcu::TestLog::EndMessage;
break;
case GL_TESS_EVALUATION_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Tessellation Evaluation Shader ***"
<< tcu::TestLog::EndMessage;
break;
case GL_GEOMETRY_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Geometry Shader ***" << tcu::TestLog::EndMessage;
break;
case GL_FRAGMENT_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Fragment Shader ***" << tcu::TestLog::EndMessage;
break;
case GL_COMPUTE_SHADER:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Compute Shader ***" << tcu::TestLog::EndMessage;
break;
default:
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "*** Unknown Shader ***" << tcu::TestLog::EndMessage;
break;
}
GLint res;
glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &res);
if (res != GL_TRUE)
compile_status = res;
GLint length;
glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &length);
if (length > 0)
{
std::vector<GLchar> source(length);
glGetShaderSource(shaders[i], length, NULL, &source[0]);
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << &source[0] << tcu::TestLog::EndMessage;
}
glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &length);
if (length > 0)
{
std::vector<GLchar> log(length);
glGetShaderInfoLog(shaders[i], length, NULL, &log[0]);
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << &log[0] << tcu::TestLog::EndMessage;
}
}
}
GLint length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
if (length > 0)
{
std::vector<GLchar> log(length);
glGetProgramInfoLog(program, length, NULL, &log[0]);
m_context.getTestContext().getLog() << tcu::TestLog::Message << &log[0] << tcu::TestLog::EndMessage;
}
}
if (compile_error)
*compile_error = (compile_status == GL_TRUE ? false : true);
if (compile_status != GL_TRUE)
return false;
return status == GL_TRUE ? true : false;
}
GLuint CreateComputeProgram(const std::string& cs)
{
const GLuint p = glCreateProgram();
if (!cs.empty())
{
const GLuint sh = glCreateShader(GL_COMPUTE_SHADER);
glAttachShader(p, sh);
glDeleteShader(sh);
const char* const src[2] = { kGLSLVer, cs.c_str() };
glShaderSource(sh, 2, src, NULL);
glCompileShader(sh);
}
return p;
}
GLuint CreateProgram(const std::string& vs, const std::string& fs)
{
const GLuint p = glCreateProgram();
if (!vs.empty())
{
const GLuint sh = glCreateShader(GL_VERTEX_SHADER);
glAttachShader(p, sh);
glDeleteShader(sh);
const char* const src[2] = { kGLSLVer, vs.c_str() };
glShaderSource(sh, 2, src, NULL);
glCompileShader(sh);
}
if (!fs.empty())
{
const GLuint sh = glCreateShader(GL_FRAGMENT_SHADER);
glAttachShader(p, sh);
glDeleteShader(sh);
const char* const src[2] = { kGLSLVer, fs.c_str() };
glShaderSource(sh, 2, src, NULL);
glCompileShader(sh);
}
return p;
}
GLuint BuildShaderProgram(GLenum type, const std::string& source)
{
const char* const src[2] = { kGLSLVer, source.c_str() };
return glCreateShaderProgramv(type, 2, src);
}
GLfloat distance(GLfloat p0, GLfloat p1)
{
return de::abs(p0 - p1);
}
inline bool ColorEqual(const vec4& c0, const vec4& c1, const vec4& epsilon)
{
if (distance(c0.x(), c1.x()) > epsilon.x())
return false;
if (distance(c0.y(), c1.y()) > epsilon.y())
return false;
if (distance(c0.z(), c1.z()) > epsilon.z())
return false;
if (distance(c0.w(), c1.w()) > epsilon.w())
return false;
return true;
}
inline bool ColorEqual(const vec3& c0, const vec3& c1, const vec4& epsilon)
{
if (distance(c0.x(), c1.x()) > epsilon.x())
return false;
if (distance(c0.y(), c1.y()) > epsilon.y())
return false;
if (distance(c0.z(), c1.z()) > epsilon.z())
return false;
return true;
}
bool ValidateReadBuffer(int x, int y, int w, int h, const vec4& expected)
{
std::vector<vec4> display(w * h);
glReadPixels(x, y, w, h, GL_RGBA, GL_FLOAT, &display[0]);
for (int j = 0; j < h; ++j)
{
for (int i = 0; i < w; ++i)
{
if (!ColorEqual(display[j * w + i], expected, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Color at (" << (x + i) << ", " << (y + j) << ") is ["
<< display[j * w + i].x() << ", " << display[j * w + i].y() << ", " << display[j * w + i].z()
<< ", " << display[j * w + i].w() << "] should be [" << expected.x() << ", " << expected.y()
<< ", " << expected.z() << ", " << expected.w() << "]." << tcu::TestLog::EndMessage;
return false;
}
}
}
return true;
}
bool ValidateReadBufferCenteredQuad(int width, int height, const vec3& expected)
{
bool result = true;
std::vector<vec3> fb(width * height);
glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, &fb[0]);
int startx = int(((float)width * 0.1f) + 1);
int starty = int(((float)height * 0.1f) + 1);
int endx = int((float)width - 2 * (((float)width * 0.1f) + 1) - 1);
int endy = int((float)height - 2 * (((float)height * 0.1f) + 1) - 1);
for (int y = starty; y < endy; ++y)
{
for (int x = startx; x < endx; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], expected, g_color_eps))
{
return false;
}
}
}
if (!ColorEqual(fb[2 * width + 2], vec3(0), g_color_eps))
{
result = false;
}
if (!ColorEqual(fb[2 * width + (width - 3)], vec3(0), g_color_eps))
{
result = false;
}
if (!ColorEqual(fb[(height - 3) * width + (width - 3)], vec3(0), g_color_eps))
{
result = false;
}
if (!ColorEqual(fb[(height - 3) * width + 2], vec3(0), g_color_eps))
{
result = false;
}
return result;
}
int getWindowWidth()
{
return renderTarget.getWidth();
}
int getWindowHeight()
{
return renderTarget.getHeight();
}
bool ValidateWindow4Quads(const vec3& lb, const vec3& rb, const vec3& rt, const vec3& lt)
{
int width = 100;
int height = 100;
std::vector<vec3> fb(width * height);
glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, &fb[0]);
bool status = true;
// left-bottom quad
for (int y = 10; y < height / 2 - 10; ++y)
{
for (int x = 10; x < width / 2 - 10; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], lb, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "First bad color (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
// right-bottom quad
for (int y = 10; y < height / 2 - 10; ++y)
{
for (int x = width / 2 + 10; x < width - 10; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], rb, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Bad color at (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
// right-top quad
for (int y = height / 2 + 10; y < height - 10; ++y)
{
for (int x = width / 2 + 10; x < width - 10; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], rt, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Bad color at (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
// left-top quad
for (int y = height / 2 + 10; y < height - 10; ++y)
{
for (int x = 10; x < width / 2 - 10; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], lt, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Bad color at (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
// middle horizontal line should be black
for (int y = height / 2 - 2; y < height / 2 + 2; ++y)
{
for (int x = 0; x < width; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], vec3(0), g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Bad color at (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
// middle vertical line should be black
for (int y = 0; y < height; ++y)
{
for (int x = width / 2 - 2; x < width / 2 + 2; ++x)
{
const int idx = y * width + x;
if (!ColorEqual(fb[idx], vec3(0), g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Bad color at (" << x << ", " << y << "): " << fb[idx].x() << " "
<< fb[idx].y() << " " << fb[idx].z() << tcu::TestLog::EndMessage;
status = false;
}
}
}
return status;
}
bool IsEqual(vec4 a, vec4 b)
{
return (a.x() == b.x()) && (a.y() == b.y()) && (a.z() == b.z()) && (a.w() == b.w());
}
bool IsEqual(uvec4 a, uvec4 b)
{
return (a.x() == b.x()) && (a.y() == b.y()) && (a.z() == b.z()) && (a.w() == b.w());
}
};
class SimpleCompute : public ComputeShaderBase
{
virtual std::string Title()
{
return "Simplest possible Compute Shader";
}
virtual std::string Purpose()
{
return "1. Verify that CS can be created, compiled and linked.\n"
"2. Verify that local work size can be queried with GetProgramiv command.\n"
"3. Verify that CS can be dispatched with DispatchCompute command.\n"
"4. Verify that CS can write to SSBO.";
}
virtual std::string Method()
{
return "Create and dispatch CS. Verify SSBO content.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_buffer;
virtual long Setup()
{
const char* const glsl_cs =
NL "layout(local_size_x = 1, local_size_y = 1) in;" NL "layout(std430) buffer Output {" NL " vec4 data;" NL
"} g_out;" NL "void main() {" NL " g_out.data = vec4(1.0, 2.0, 3.0, 4.0);" NL "}";
m_program = CreateComputeProgram(glsl_cs);
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return ERROR;
GLint v[3];
glGetProgramiv(m_program, GL_COMPUTE_WORK_GROUP_SIZE, v);
if (v[0] != 1 || v[1] != 1 || v[2] != 1)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Got " << v[0] << ", " << v[1] << ", " << v[2]
<< ", expected: 1, 1, 1 in GL_COMPUTE_WORK_GROUP_SIZE check" << tcu::TestLog::EndMessage;
return ERROR;
}
glGenBuffers(1, &m_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4), NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
return NO_ERROR;
}
virtual long Run()
{
glUseProgram(m_program);
glDispatchCompute(1, 1, 1);
vec4* data;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
data = static_cast<vec4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4), GL_MAP_READ_BIT));
long error = NO_ERROR;
if (!IsEqual(data[0], vec4(1.0f, 2.0f, 3.0f, 4.0f)))
{
error = ERROR;
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
return error;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_buffer);
return NO_ERROR;
}
};
class BasicOneWorkGroup : public ComputeShaderBase
{
virtual std::string Title()
{
return "One work group with various local sizes";
}
virtual std::string Purpose()
{
return NL "1. Verify that declared local work size has correct effect." NL
"2. Verify that the number of shader invocations is correct." NL
"3. Verify that the built-in variables: gl_WorkGroupSize, gl_WorkGroupID, gl_GlobalInvocationID," NL
" gl_LocalInvocationID and gl_LocalInvocationIndex has correct values." NL
"4. Verify that DispatchCompute and DispatchComputeIndirect commands work as expected.";
}
virtual std::string Method()
{
return NL "1. Create several CS with various local sizes." NL
"2. Dispatch each CS with DispatchCompute and DispatchComputeIndirect commands." NL
"3. Verify SSBO content.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
GLuint m_dispatch_buffer;
std::string GenSource(int x, int y, int z, GLuint binding)
{
std::stringstream ss;
ss << NL "layout(local_size_x = " << x << ", local_size_y = " << y << ", local_size_z = " << z
<< ") in;" NL "layout(std430, binding = " << binding
<< ") buffer Output {" NL " uvec4 local_id[];" NL "} g_out;" NL "void main() {" NL
" if (gl_WorkGroupSize == uvec3("
<< x << ", " << y << ", " << z
<< ") && gl_WorkGroupID == uvec3(0) &&" NL " gl_GlobalInvocationID == gl_LocalInvocationID) {" NL
" g_out.local_id[gl_LocalInvocationIndex] = uvec4(gl_LocalInvocationID, 0);" NL " } else {" NL
" g_out.local_id[gl_LocalInvocationIndex] = uvec4(0xffff);" NL " }" NL "}";
return ss.str();
}
bool RunIteration(int local_size_x, int local_size_y, int local_size_z, GLuint binding, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size_x, local_size_y, local_size_z, binding));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
GLint v[3];
glGetProgramiv(m_program, GL_COMPUTE_WORK_GROUP_SIZE, v);
if (v[0] != local_size_x || v[1] != local_size_y || v[2] != local_size_z)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "GL_COMPUTE_LOCAL_WORK_SIZE is (" << v[0] << " " << v[1] << " " << v[2]
<< ") should be (" << local_size_x << " " << local_size_y << " " << local_size_z << ")"
<< tcu::TestLog::EndMessage;
return false;
}
const int kSize = local_size_x * local_size_y * local_size_z;
if (m_storage_buffer == 0)
glGenBuffers(1, &m_storage_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uvec4) * kSize, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
glUseProgram(m_program);
if (dispatch_indirect)
{
const GLuint num_groups[3] = { 1, 1, 1 };
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), num_groups, GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(1, 1, 1);
}
uvec4* data;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
data =
static_cast<uvec4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kSize * sizeof(uvec4), GL_MAP_READ_BIT));
bool ret = true;
for (int z = 0; z < local_size_z; ++z)
{
for (int y = 0; y < local_size_y; ++y)
{
for (int x = 0; x < local_size_x; ++x)
{
const int index = z * local_size_x * local_size_y + y * local_size_x + x;
if (!IsEqual(data[index], uvec4(x, y, z, 0)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Invalid data at offset " << index << tcu::TestLog::EndMessage;
ret = false;
}
}
}
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
return ret;
}
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
if (!RunIteration(16, 1, 1, 0, true))
return ERROR;
if (!RunIteration(8, 8, 1, 1, false))
return ERROR;
if (!RunIteration(4, 4, 4, 2, true))
return ERROR;
if (!RunIteration(1, 2, 3, 3, false))
return ERROR;
if (!RunIteration(1024, 1, 1, 3, true))
return ERROR;
if (!RunIteration(16, 8, 8, 3, false))
return ERROR;
if (!RunIteration(32, 1, 32, 7, true))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_storage_buffer);
glDeleteBuffers(1, &m_dispatch_buffer);
return NO_ERROR;
}
};
class BasicResourceUBO : public ComputeShaderBase
{
virtual std::string Title()
{
return "Compute Shader resources - UBOs";
}
virtual std::string Purpose()
{
return "Verify that CS is able to read data from UBOs and write it to SSBO.";
}
virtual std::string Method()
{
return NL "1. Create CS which uses array of UBOs." NL
"2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL
"3. Read data from each UBO and write it to SSBO." NL "4. Verify SSBO content." NL
"5. Repeat for different buffer and CS work sizes.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
GLuint m_uniform_buffer[12];
GLuint m_dispatch_buffer;
std::string GenSource(const uvec3& local_size, const uvec3& num_groups)
{
const uvec3 global_size = local_size * num_groups;
std::stringstream ss;
ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y()
<< ", local_size_z = " << local_size.z() << ") in;" NL "const uvec3 kGlobalSize = uvec3(" << global_size.x()
<< ", " << global_size.y() << ", " << global_size.z()
<< ");" NL "layout(std140) uniform InputBuffer {" NL " vec4 data["
<< global_size.x() * global_size.y() * global_size.z()
<< "];" NL "} g_in_buffer[12];" NL "layout(std430) buffer OutputBuffer {" NL " vec4 data0["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data1["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data2["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data3["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data4["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data5["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data6["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data7["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data8["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data9["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data10["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data11["
<< global_size.x() * global_size.y() * global_size.z()
<< "];" NL "} g_out_buffer;" NL "void main() {" NL " const uint global_index = gl_GlobalInvocationID.x +" NL
" gl_GlobalInvocationID.y * kGlobalSize.x +" NL
" gl_GlobalInvocationID.z * kGlobalSize.x * kGlobalSize.y;" NL
" g_out_buffer.data0[global_index] = g_in_buffer[0].data[global_index];" NL
" g_out_buffer.data1[global_index] = g_in_buffer[1].data[global_index];" NL
" g_out_buffer.data2[global_index] = g_in_buffer[2].data[global_index];" NL
" g_out_buffer.data3[global_index] = g_in_buffer[3].data[global_index];" NL
" g_out_buffer.data4[global_index] = g_in_buffer[4].data[global_index];" NL
" g_out_buffer.data5[global_index] = g_in_buffer[5].data[global_index];" NL
" g_out_buffer.data6[global_index] = g_in_buffer[6].data[global_index];" NL
" g_out_buffer.data7[global_index] = g_in_buffer[7].data[global_index];" NL
" g_out_buffer.data8[global_index] = g_in_buffer[8].data[global_index];" NL
" g_out_buffer.data9[global_index] = g_in_buffer[9].data[global_index];" NL
" g_out_buffer.data10[global_index] = g_in_buffer[10].data[global_index];" NL
" g_out_buffer.data11[global_index] = g_in_buffer[11].data[global_index];" NL "}";
return ss.str();
}
bool RunIteration(const uvec3& local_size, const uvec3& num_groups, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size, num_groups));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
for (GLuint i = 0; i < 12; ++i)
{
char name[32];
sprintf(name, "InputBuffer[%u]", i);
const GLuint index = glGetUniformBlockIndex(m_program, name);
glUniformBlockBinding(m_program, index, i);
GLint p = 0;
glGetActiveUniformBlockiv(m_program, index, GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER, &p);
if (p == GL_FALSE)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER should be TRUE."
<< tcu::TestLog::EndMessage;
return false;
}
}
const GLuint kBufferSize =
local_size.x() * num_groups.x() * local_size.y() * num_groups.y() * local_size.z() * num_groups.z();
if (m_storage_buffer == 0)
glGenBuffers(1, &m_storage_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * kBufferSize * 12, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
if (m_uniform_buffer[0] == 0)
glGenBuffers(12, m_uniform_buffer);
for (GLuint i = 0; i < 12; ++i)
{
std::vector<vec4> data(kBufferSize);
for (GLuint j = 0; j < kBufferSize; ++j)
{
data[j] = vec4(static_cast<float>(i * kBufferSize + j));
}
glBindBufferBase(GL_UNIFORM_BUFFER, i, m_uniform_buffer[i]);
glBufferData(GL_UNIFORM_BUFFER, sizeof(vec4) * kBufferSize, &data[0], GL_DYNAMIC_DRAW);
}
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glUseProgram(m_program);
if (dispatch_indirect)
{
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), &num_groups[0], GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(num_groups.x(), num_groups.y(), num_groups.z());
}
std::vector<vec4> data(kBufferSize * 12);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * kBufferSize * 12, &data[0]);
for (GLuint z = 0; z < local_size.z() * num_groups.z(); ++z)
{
for (GLuint y = 0; y < local_size.y() * num_groups.y(); ++y)
{
for (GLuint x = 0; x < local_size.x() * num_groups.x(); ++x)
{
const GLuint index = z * local_size.x() * num_groups.x() * local_size.y() * num_groups.y() +
y * local_size.x() * num_groups.x() + x;
for (int i = 0; i < 1; ++i)
{
if (!IsEqual(data[index * 12 + i], vec4(static_cast<float>(index * 12 + i))))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Incorrect data at offset "
<< index * 12 + i << "." << tcu::TestLog::EndMessage;
return false;
}
}
}
}
}
return true;
}
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
memset(m_uniform_buffer, 0, sizeof(m_uniform_buffer));
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
if (!RunIteration(uvec3(64, 1, 1), uvec3(8, 1, 1), false))
return ERROR;
if (!RunIteration(uvec3(2, 2, 2), uvec3(2, 2, 2), true))
return ERROR;
if (!RunIteration(uvec3(2, 4, 2), uvec3(2, 4, 1), false))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_storage_buffer);
glDeleteBuffers(12, m_uniform_buffer);
glDeleteBuffers(1, &m_dispatch_buffer);
return NO_ERROR;
}
};
class BasicResourceTexture : public ComputeShaderBase
{
virtual std::string Title()
{
return NL "Compute Shader resources - Textures";
}
virtual std::string Purpose()
{
return NL "Verify that texture access works correctly in CS.";
}
virtual std::string Method()
{
return NL "1. Create CS which uses all sampler types (sampler1D, sampler2D, sampler3D, sampler2DRect," NL
" sampler1DArray, sampler2DArray, samplerBuffer, sampler2DMS, sampler2DMSArray)." NL
"2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL
"3. Sample each texture and write sampled value to SSBO." NL "4. Verify SSBO content." NL
"5. Repeat for different texture and CS work sizes.";
}
virtual std::string PassCriteria()
{
return NL "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
GLuint m_texture[9];
GLuint m_texture_buffer;
GLuint m_dispatch_buffer;
std::string GenSource(const uvec3& local_size, const uvec3& num_groups)
{
const uvec3 global_size = local_size * num_groups;
std::stringstream ss;
ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y()
<< ", local_size_z = " << local_size.z() << ") in;" NL "const uvec3 kGlobalSize = uvec3(" << global_size.x()
<< ", " << global_size.y() << ", " << global_size.z()
<< ");" NL "uniform sampler1D g_sampler0;" NL "uniform sampler2D g_sampler1;" NL
"uniform sampler3D g_sampler2;" NL "uniform sampler2DRect g_sampler3;" NL
"uniform sampler1DArray g_sampler4;" NL "uniform sampler2DArray g_sampler5;" NL
"uniform samplerBuffer g_sampler6;" NL "uniform sampler2DMS g_sampler7;" NL
"uniform sampler2DMSArray g_sampler8;" NL "layout(std430) buffer OutputBuffer {" NL " vec4 data0["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data1["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data2["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data3["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data4["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data5["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data6["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data7["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " vec4 data8["
<< global_size.x() * global_size.y() * global_size.z()
<< "];" NL "} g_out_buffer;" NL "void main() {" NL " const uint global_index = gl_GlobalInvocationID.x +" NL
" gl_GlobalInvocationID.y * kGlobalSize.x +" NL
" gl_GlobalInvocationID.z * kGlobalSize.x * kGlobalSize.y;" NL
" g_out_buffer.data0[global_index] = texelFetch(g_sampler0, int(gl_GlobalInvocationID), 0);" NL
" g_out_buffer.data1[global_index] = texture(g_sampler1, vec2(gl_GlobalInvocationID) / "
"vec2(kGlobalSize));" NL " g_out_buffer.data2[global_index] = textureProj(g_sampler2, "
"vec4(vec3(gl_GlobalInvocationID) / vec3(kGlobalSize), 1.0));" NL
" g_out_buffer.data3[global_index] = textureProjOffset(g_sampler3, vec3(vec2(gl_GlobalInvocationID), "
"1.0), ivec2(0));" NL " g_out_buffer.data4[global_index] = textureLodOffset(g_sampler4, "
"vec2(gl_GlobalInvocationID.x / kGlobalSize.x, gl_GlobalInvocationID.y), 0.0, "
"0);" NL " g_out_buffer.data5[global_index] = texelFetchOffset(g_sampler5, "
"ivec3(gl_GlobalInvocationID), 0, ivec2(0));" NL
" g_out_buffer.data6[global_index] = texelFetch(g_sampler6, int(global_index));" NL
" g_out_buffer.data7[global_index] = texelFetch(g_sampler7, ivec2(gl_GlobalInvocationID), 1);" NL
" g_out_buffer.data8[global_index] = texelFetch(g_sampler8, ivec3(gl_GlobalInvocationID), 2);" NL "}";
return ss.str();
}
bool RunIteration(const uvec3& local_size, const uvec3& num_groups, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size, num_groups));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
glUseProgram(m_program);
for (int i = 0; i < 9; ++i)
{
char name[32];
sprintf(name, "g_sampler%d", i);
glUniform1i(glGetUniformLocation(m_program, name), i);
}
glUseProgram(0);
const GLuint kBufferSize =
local_size.x() * num_groups.x() * local_size.y() * num_groups.y() * local_size.z() * num_groups.z();
const GLint kWidth = static_cast<GLint>(local_size.x() * num_groups.x());
const GLint kHeight = static_cast<GLint>(local_size.y() * num_groups.y());
const GLint kDepth = static_cast<GLint>(local_size.z() * num_groups.z());
std::vector<vec4> buffer_data(kBufferSize * 9);
if (m_storage_buffer == 0)
glGenBuffers(1, &m_storage_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * kBufferSize * 9, &buffer_data[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
std::vector<vec4> texture_data(kBufferSize, vec4(123.0f));
if (m_texture[0] == 0)
glGenTextures(9, m_texture);
if (m_texture_buffer == 0)
glGenBuffers(1, &m_texture_buffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_1D, m_texture[0]);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, kWidth, 0, GL_RGBA, GL_FLOAT, &texture_data[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kWidth, kHeight, 0, GL_RGBA, GL_FLOAT, &texture_data[0]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_3D, m_texture[2]);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA32F, kWidth, kHeight, kDepth, 0, GL_RGBA, GL_FLOAT, &texture_data[0]);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_RECTANGLE, m_texture[3]);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA32F, kWidth, kHeight, 0, GL_RGBA, GL_FLOAT, &texture_data[0]);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_1D_ARRAY, m_texture[4]);
glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, kWidth, kHeight, 0, GL_RGBA, GL_FLOAT, &texture_data[0]);
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture[5]);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA32F, kWidth, kHeight, kDepth, 0, GL_RGBA, GL_FLOAT,
&texture_data[0]);
glActiveTexture(GL_TEXTURE6);
glBindBuffer(GL_TEXTURE_BUFFER, m_texture_buffer);
glBufferData(GL_TEXTURE_BUFFER, kBufferSize * sizeof(vec4), &texture_data[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
glBindTexture(GL_TEXTURE_BUFFER, m_texture[6]);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_texture_buffer);
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_texture[7]);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA32F, kWidth, kHeight, GL_FALSE);
glActiveTexture(GL_TEXTURE8);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, m_texture[8]);
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, GL_RGBA32F, kWidth, kHeight, kDepth, GL_FALSE);
// clear MS textures
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture[7], 0);
glClearBufferfv(GL_COLOR, 0, &vec4(123.0f)[0]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture[8], 0);
glClearBufferfv(GL_COLOR, 0, &vec4(123.0f)[0]);
glDeleteFramebuffers(1, &fbo);
glUseProgram(m_program);
if (dispatch_indirect)
{
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), &num_groups[0], GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(num_groups.x(), num_groups.y(), num_groups.z());
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * kBufferSize * 9, &buffer_data[0]);
for (GLuint index = 0; index < kBufferSize * 9; ++index)
{
if (!IsEqual(buffer_data[index], vec4(123.0f)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Incorrect data at index " << index << "." << tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
memset(m_texture, 0, sizeof(m_texture));
m_texture_buffer = 0;
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
if (!RunIteration(uvec3(4, 4, 4), uvec3(8, 1, 1), false))
return ERROR;
if (!RunIteration(uvec3(2, 4, 2), uvec3(2, 4, 1), true))
return ERROR;
if (!RunIteration(uvec3(2, 2, 2), uvec3(2, 2, 2), false))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glActiveTexture(GL_TEXTURE0);
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_storage_buffer);
glDeleteTextures(9, m_texture);
glDeleteBuffers(1, &m_texture_buffer);
glDeleteBuffers(1, &m_dispatch_buffer);
return NO_ERROR;
}
};
class BasicResourceImage : public ComputeShaderBase
{
virtual std::string Title()
{
return NL "Compute Shader resources - Images";
}
virtual std::string Purpose()
{
return NL "Verify that reading/writing GPU memory via image variables work as expected.";
}
virtual std::string Method()
{
return NL "1. Create CS which uses two image2D variables to read and write underlying GPU memory." NL
"2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL
"3. Verify memory content." NL "4. Repeat for different texture and CS work sizes.";
}
virtual std::string PassCriteria()
{
return NL "Everything works as expected.";
}
GLuint m_program;
GLuint m_draw_program;
GLuint m_texture[2];
GLuint m_dispatch_buffer;
GLuint m_vertex_array;
std::string GenSource(const uvec3& local_size, const uvec3& num_groups)
{
const uvec3 global_size = local_size * num_groups;
std::stringstream ss;
ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y()
<< ", local_size_z = " << local_size.z()
<< ") in;" NL "layout(rgba32f) coherent uniform image2D g_image1;" NL
"layout(rgba32f) uniform image2D g_image2;" NL "const uvec3 kGlobalSize = uvec3("
<< global_size.x() << ", " << global_size.y() << ", " << global_size.z()
<< ");" NL "void main() {" NL
" if (gl_GlobalInvocationID.x >= kGlobalSize.x || gl_GlobalInvocationID.y >= kGlobalSize.y) return;" NL
" vec4 color = vec4(gl_GlobalInvocationID.x + gl_GlobalInvocationID.y) / 255.0;" NL
" imageStore(g_image1, ivec2(gl_GlobalInvocationID), color);" NL
" vec4 c = imageLoad(g_image1, ivec2(gl_GlobalInvocationID));" NL
" imageStore(g_image2, ivec2(gl_GlobalInvocationID), c);" NL "}";
return ss.str();
}
bool RunIteration(const uvec3& local_size, const uvec3& num_groups, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size, num_groups));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
glUseProgram(m_program);
glUniform1i(glGetUniformLocation(m_program, "g_image1"), 0);
glUniform1i(glGetUniformLocation(m_program, "g_image2"), 1);
glUseProgram(0);
const GLint kWidth = static_cast<GLint>(local_size.x() * num_groups.x());
const GLint kHeight = static_cast<GLint>(local_size.y() * num_groups.y());
const GLint kDepth = static_cast<GLint>(local_size.z() * num_groups.z());
const GLuint kSize = kWidth * kHeight * kDepth;
std::vector<vec4> data(kSize);
if (m_texture[0] == 0)
glGenTextures(2, m_texture);
for (int i = 0; i < 2; ++i)
{
glBindTexture(GL_TEXTURE_2D, m_texture[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kWidth, kHeight, 0, GL_RGBA, GL_FLOAT, &data[0]);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glUseProgram(m_program);
if (dispatch_indirect)
{
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), &num_groups[0], GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(num_groups.x(), num_groups.y(), num_groups.z());
}
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texture[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glUseProgram(m_draw_program);
glBindVertexArray(m_vertex_array);
glViewport(0, 0, kWidth, kHeight);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 1);
std::vector<vec4> display(kWidth * kHeight);
glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_FLOAT, &display[0]);
for (int y = 0; y < kHeight; ++y)
{
for (int x = 0; x < kWidth; ++x)
{
if (y >= getWindowHeight() || x >= getWindowWidth())
{
continue;
}
const vec4 c = vec4(float(y + x) / 255.0f);
if (!ColorEqual(display[y * kWidth + x], c, g_color_eps))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Got " << display[y * kWidth + x].x() << ", "
<< display[y * kWidth + x].y() << ", " << display[y * kWidth + x].z() << ", "
<< display[y * kWidth + x].w() << ", expected " << c.x() << ", " << c.y() << ", " << c.z()
<< ", " << c.w() << " at " << x << ", " << y << tcu::TestLog::EndMessage;
return false;
}
}
}
return true;
}
virtual long Setup()
{
m_program = 0;
memset(m_texture, 0, sizeof(m_texture));
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
const char* const glsl_vs =
NL "out StageData {" NL " vec2 texcoord;" NL "} vs_out;" NL
"const vec2 g_quad[] = vec2[](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));" NL "void main() {" NL
" gl_Position = vec4(g_quad[gl_VertexID], 0, 1);" NL
" vs_out.texcoord = 0.5 + 0.5 * g_quad[gl_VertexID];" NL "}";
const char* glsl_fs =
NL "in StageData {" NL " vec2 texcoord;" NL "} fs_in;" NL "layout(location = 0) out vec4 o_color;" NL
"uniform sampler2D g_image1;" NL "uniform sampler2D g_image2;" NL "void main() {" NL
" vec4 c1 = texture(g_image1, fs_in.texcoord);" NL " vec4 c2 = texture(g_image2, fs_in.texcoord);" NL
" if (c1 == c2) o_color = c1;" NL " else o_color = vec4(1, 0, 0, 1);" NL "}";
m_draw_program = CreateProgram(glsl_vs, glsl_fs);
glLinkProgram(m_draw_program);
if (!CheckProgram(m_draw_program))
return ERROR;
glUseProgram(m_draw_program);
glUniform1i(glGetUniformLocation(m_draw_program, "g_image1"), 0);
glUniform1i(glGetUniformLocation(m_draw_program, "g_image2"), 1);
glUseProgram(0);
glGenVertexArrays(1, &m_vertex_array);
if (!pixelFormat.alphaBits)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Test requires default framebuffer alpha bits" << tcu::TestLog::EndMessage;
return NO_ERROR;
}
if (!RunIteration(uvec3(8, 16, 1), uvec3(8, 4, 1), true))
return ERROR;
if (!RunIteration(uvec3(4, 32, 1), uvec3(16, 2, 1), false))
return ERROR;
if (!RunIteration(uvec3(16, 4, 1), uvec3(4, 16, 1), false))
return ERROR;
if (!RunIteration(uvec3(8, 8, 1), uvec3(8, 8, 1), true))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteProgram(m_draw_program);
glDeleteVertexArrays(1, &m_vertex_array);
glDeleteTextures(2, m_texture);
glDeleteBuffers(1, &m_dispatch_buffer);
glViewport(0, 0, getWindowWidth(), getWindowHeight());
return NO_ERROR;
}
};
class BasicResourceAtomicCounter : public ComputeShaderBase
{
virtual std::string Title()
{
return "Compute Shader resources - Atomic Counters";
}
virtual std::string Purpose()
{
return NL
"1. Verify that Atomic Counters work as expected in CS." NL
"2. Verify that built-in functions: atomicCounterIncrement and atomicCounterDecrement work correctly." NL
"3. Verify that GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER is accepted by" NL
" GetActiveAtomicCounterBufferiv command.";
}
virtual std::string Method()
{
return NL
"1. Create CS which uses two atomic_uint variables." NL
"2. In CS write values returned by atomicCounterIncrement and atomicCounterDecrement functions to SSBO." NL
"3. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL "4. Verify SSBO content." NL
"5. Repeat for different buffer and CS work sizes.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
GLuint m_counter_buffer[2];
GLuint m_dispatch_buffer;
std::string GenSource(const uvec3& local_size, const uvec3& num_groups)
{
const uvec3 global_size = local_size * num_groups;
std::stringstream ss;
ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y()
<< ", local_size_z = " << local_size.z()
<< ") in;" NL "layout(std430, binding = 0) buffer Output {" NL " uint inc_data["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uint dec_data["
<< global_size.x() * global_size.y() * global_size.z()
<< "];" NL "};" NL "layout(binding = 0, offset = 0) uniform atomic_uint g_inc_counter;" NL
"layout(binding = 1, offset = 0) uniform atomic_uint g_dec_counter;" NL "void main() {" NL
" const uint index = atomicCounterIncrement(g_inc_counter);" NL " inc_data[index] = index;" NL
" dec_data[index] = atomicCounterDecrement(g_dec_counter);" NL "}";
return ss.str();
}
bool RunIteration(const uvec3& local_size, const uvec3& num_groups, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size, num_groups));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
GLint p[2] = { 0 };
glGetActiveAtomicCounterBufferiv(m_program, 0, GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER, &p[0]);
glGetActiveAtomicCounterBufferiv(m_program, 1, GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER, &p[1]);
if (p[0] == GL_FALSE || p[1] == GL_FALSE)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER should be TRUE."
<< tcu::TestLog::EndMessage;
return false;
}
const GLint kWidth = static_cast<GLint>(local_size.x() * num_groups.x());
const GLint kHeight = static_cast<GLint>(local_size.y() * num_groups.y());
const GLint kDepth = static_cast<GLint>(local_size.z() * num_groups.z());
const GLuint kSize = kWidth * kHeight * kDepth;
if (m_storage_buffer == 0)
glGenBuffers(1, &m_storage_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint) * kSize * 2, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
if (m_counter_buffer[0] == 0)
glGenBuffers(2, m_counter_buffer);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_counter_buffer[0]);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_STREAM_DRAW);
*static_cast<GLuint*>(glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY)) = 0;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, m_counter_buffer[1]);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_STREAM_DRAW);
*static_cast<GLuint*>(glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY)) = kSize;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
glUseProgram(m_program);
if (dispatch_indirect)
{
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), &num_groups[0], GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(num_groups.x(), num_groups.y(), num_groups.z());
}
std::vector<GLuint> data(kSize);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * kSize, &data[0]);
for (GLuint i = 0; i < kSize; ++i)
{
if (data[i] != i)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value at index " << i << " is "
<< data[i] << " should be " << i << "." << tcu::TestLog::EndMessage;
return false;
}
}
GLuint value;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_counter_buffer[0]);
glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &value);
if (value != kSize)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Final atomic counter value (buffer 0) is "
<< value << " should be " << kSize << "." << tcu::TestLog::EndMessage;
return false;
}
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_counter_buffer[1]);
glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &value);
if (value != 0)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Final atomic counter value (buffer 1) is "
<< value << " should be 0." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
memset(m_counter_buffer, 0, sizeof(m_counter_buffer));
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
if (!RunIteration(uvec3(4, 3, 2), uvec3(2, 3, 4), false))
return ERROR;
if (!RunIteration(uvec3(1, 1, 1), uvec3(1, 1, 1), true))
return ERROR;
if (!RunIteration(uvec3(1, 6, 1), uvec3(1, 1, 8), false))
return ERROR;
if (!RunIteration(uvec3(4, 1, 2), uvec3(10, 3, 4), true))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(2, m_counter_buffer);
glDeleteBuffers(1, &m_dispatch_buffer);
glDeleteBuffers(1, &m_storage_buffer);
return NO_ERROR;
}
};
class BasicResourceSubroutine : public ComputeShaderBase
{
virtual std::string Title()
{
return "Compute Shader resources - Subroutines";
}
virtual std::string Purpose()
{
return NL "1. Verify that subroutines work as expected in CS." NL
"2. Verify that subroutines array can be indexed with gl_WorkGroupID built-in variable." NL
"3. Verify that atomicCounterIncrement, imageLoad and texelFetch functions" NL
" work as expected when called in CS from subroutine.";
}
virtual std::string Method()
{
return NL "1. Create CS which uses array of subroutines." NL
"2. In CS index subroutine array with gl_WorkGroupID built-in variable." NL
"3. In each subroutine load data from SSBO0 and write it to SSBO1." NL
"3. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL
"4. Verify SSBO1 content." NL "5. Repeat for different buffer and CS work sizes.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_atomic_counter_buffer;
GLuint m_storage_buffer[2];
GLuint m_buffer[2];
GLuint m_texture_buffer[2];
virtual long Setup()
{
m_program = 0;
m_atomic_counter_buffer = 0;
memset(m_storage_buffer, 0, sizeof(m_storage_buffer));
memset(m_buffer, 0, sizeof(m_buffer));
memset(m_texture_buffer, 0, sizeof(m_texture_buffer));
return NO_ERROR;
}
virtual long Run()
{
const char* const glsl_cs =
NL "layout(local_size_x = 16) in;" NL "layout(binding = 1, std430) buffer Input {" NL " uvec4 data[16];" NL
"} g_input;" NL "layout(std430, binding = 0) buffer Output {" NL " uvec4 g_output[64];" NL "};" NL
"subroutine void ComputeType();" NL "subroutine uniform ComputeType Compute[4];" NL
"layout(binding = 0, offset = 0) uniform atomic_uint g_atomic_counter;" NL
"layout(rgba32ui) readonly uniform uimageBuffer g_image_buffer;" NL
"uniform usamplerBuffer g_sampler_buffer;" NL "subroutine(ComputeType)" NL "void Compute0() {" NL
" const uint index = atomicCounterIncrement(g_atomic_counter);" NL
" g_output[index] = uvec4(index);" NL "}" NL "subroutine(ComputeType)" NL "void Compute1() {" NL
" g_output[gl_GlobalInvocationID.x] = g_input.data[gl_LocalInvocationIndex];" NL "}" NL
"subroutine(ComputeType)" NL "void Compute2() {" NL
" g_output[gl_GlobalInvocationID.x] = imageLoad(g_image_buffer, int(gl_LocalInvocationIndex));" NL
"}" NL "subroutine(ComputeType)" NL "void Compute3() {" NL
" g_output[gl_GlobalInvocationID.x] = texelFetch(g_sampler_buffer, int(gl_LocalInvocationIndex));" NL
"}" NL "void main() {" NL " Compute[gl_WorkGroupID.x]();" NL "}";
m_program = CreateComputeProgram(glsl_cs);
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return ERROR;
glGenBuffers(2, m_storage_buffer);
/* output buffer */
{
std::vector<uvec4> data(64, uvec4(0xffff));
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uvec4) * 64, &data[0], GL_DYNAMIC_DRAW);
}
/* input buffer */
{
std::vector<uvec4> data(16);
for (GLuint i = 0; i < 16; ++i)
data[i] = uvec4(i + 16);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[1]);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uvec4) * 16, &data[0], GL_DYNAMIC_DRAW);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
glGenBuffers(1, &m_atomic_counter_buffer);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_atomic_counter_buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_STREAM_DRAW);
*static_cast<GLuint*>(glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY)) = 0;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glGenBuffers(2, m_buffer);
/* image buffer */
{
std::vector<uvec4> data(16);
for (GLuint i = 0; i < 16; ++i)
data[i] = uvec4(i + 32);
glBindBuffer(GL_TEXTURE_BUFFER, m_buffer[0]);
glBufferData(GL_TEXTURE_BUFFER, sizeof(uvec4) * 16, &data[0], GL_STATIC_DRAW);
}
/* texture buffer */
{
std::vector<uvec4> data(16);
for (GLuint i = 0; i < 16; ++i)
data[i] = uvec4(i + 48);
glBindBuffer(GL_TEXTURE_BUFFER, m_buffer[1]);
glBufferData(GL_TEXTURE_BUFFER, sizeof(uvec4) * 16, &data[0], GL_STATIC_DRAW);
}
glBindBuffer(GL_TEXTURE_BUFFER, 0);
glGenTextures(2, m_texture_buffer);
glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer[0]);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, m_buffer[0]);
glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer[1]);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, m_buffer[1]);
glBindTexture(GL_TEXTURE_BUFFER, 0);
const GLuint index_compute0 = glGetSubroutineIndex(m_program, GL_COMPUTE_SHADER, "Compute0");
const GLuint index_compute1 = glGetSubroutineIndex(m_program, GL_COMPUTE_SHADER, "Compute1");
const GLuint index_compute2 = glGetSubroutineIndex(m_program, GL_COMPUTE_SHADER, "Compute2");
const GLuint index_compute3 = glGetSubroutineIndex(m_program, GL_COMPUTE_SHADER, "Compute3");
const GLint loc_compute0 = glGetSubroutineUniformLocation(m_program, GL_COMPUTE_SHADER, "Compute[0]");
const GLint loc_compute1 = glGetSubroutineUniformLocation(m_program, GL_COMPUTE_SHADER, "Compute[1]");
const GLint loc_compute2 = glGetSubroutineUniformLocation(m_program, GL_COMPUTE_SHADER, "Compute[2]");
const GLint loc_compute3 = glGetSubroutineUniformLocation(m_program, GL_COMPUTE_SHADER, "Compute[3]");
// bind resources
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer[0]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storage_buffer[1]);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_atomic_counter_buffer);
glBindImageTexture(0, m_texture_buffer[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32UI);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_texture_buffer[1]);
glUseProgram(m_program);
// setup subroutines
GLuint indices[4];
indices[loc_compute0] = index_compute0;
indices[loc_compute1] = index_compute1;
indices[loc_compute2] = index_compute2;
indices[loc_compute3] = index_compute3;
glUniformSubroutinesuiv(GL_COMPUTE_SHADER, 4, indices);
glDispatchCompute(4, 1, 1);
std::vector<uvec4> data(64);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[0]);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(uvec4) * 64, &data[0]);
for (GLuint i = 0; i < 64; ++i)
{
if (!IsEqual(data[i], uvec4(i)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Invalid value at index " << i << "." << tcu::TestLog::EndMessage;
return ERROR;
}
}
GLuint value;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_atomic_counter_buffer);
glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &value);
if (value != 16)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Final atomic counter value is " << value
<< " should be 16." << tcu::TestLog::EndMessage;
return ERROR;
}
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_atomic_counter_buffer);
glDeleteBuffers(2, m_storage_buffer);
glDeleteBuffers(2, m_buffer);
glDeleteTextures(2, m_texture_buffer);
return NO_ERROR;
}
};
class BasicResourceUniform : public ComputeShaderBase
{
virtual std::string Title()
{
return "Compute Shader resources - Uniforms";
}
virtual std::string Purpose()
{
return NL "1. Verify that all types of uniform variables work as expected in CS." NL
"2. Verify that uniform variables can be updated with Uniform* and ProgramUniform* commands." NL
"3. Verify that re-linking CS program works as expected.";
}
virtual std::string Method()
{
return NL "1. Create CS which uses all (single precision and integer) types of uniform variables." NL
"2. Update uniform variables with ProgramUniform* commands." NL
"3. Verify that uniform variables were updated correctly." NL "4. Re-link CS program." NL
"5. Update uniform variables with Uniform* commands." NL
"6. Verify that uniform variables were updated correctly.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
const char* const glsl_cs = NL
"layout(local_size_x = 1) in;" NL "buffer Result {" NL " int g_result;" NL "};" NL "uniform float g_0;" NL
"uniform vec2 g_1;" NL "uniform vec3 g_2;" NL "uniform vec4 g_3;" NL "uniform mat2 g_4;" NL
"uniform mat2x3 g_5;" NL "uniform mat2x4 g_6;" NL "uniform mat3x2 g_7;" NL "uniform mat3 g_8;" NL
"uniform mat3x4 g_9;" NL "uniform mat4x2 g_10;" NL "uniform mat4x3 g_11;" NL "uniform mat4 g_12;" NL
"uniform int g_13;" NL "uniform ivec2 g_14;" NL "uniform ivec3 g_15;" NL "uniform ivec4 g_16;" NL
"uniform uint g_17;" NL "uniform uvec2 g_18;" NL "uniform uvec3 g_19;" NL "uniform uvec4 g_20;" NL NL
"void main() {" NL " g_result = 1;" NL NL " if (g_0 != 1.0) g_result = 0;" NL
" if (g_1 != vec2(2.0, 3.0)) g_result = 0;" NL " if (g_2 != vec3(4.0, 5.0, 6.0)) g_result = 0;" NL
" if (g_3 != vec4(7.0, 8.0, 9.0, 10.0)) g_result = 0;" NL NL
" if (g_4 != mat2(11.0, 12.0, 13.0, 14.0)) g_result = 0;" NL
" if (g_5 != mat2x3(15.0, 16.0, 17.0, 18.0, 19.0, 20.0)) g_result = 0;" NL
" if (g_6 != mat2x4(21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0)) g_result = 0;" NL NL
" if (g_7 != mat3x2(29.0, 30.0, 31.0, 32.0, 33.0, 34.0)) g_result = 0;" NL
" if (g_8 != mat3(35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0)) g_result = 0;" NL
" if (g_9 != mat3x4(44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0)) g_result = "
"0;" NL NL " if (g_10 != mat4x2(56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0)) g_result = 0;" NL
" if (g_11 != mat4x3(63.0, 64.0, 65.0, 66.0, 67.0, 68.0, 69.0, 70.0, 71.0, 27.0, 73, 74.0)) g_result = "
"0;" NL " if (g_12 != mat4(75.0, 76.0, 77.0, 78.0, 79.0, 80.0, 81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, "
"88.0, 89.0, 90.0)) g_result = 0;" NL NL " if (g_13 != 91) g_result = 0;" NL
" if (g_14 != ivec2(92, 93)) g_result = 0;" NL " if (g_15 != ivec3(94, 95, 96)) g_result = 0;" NL
" if (g_16 != ivec4(97, 98, 99, 100)) g_result = 0;" NL NL " if (g_17 != 101u) g_result = 0;" NL
" if (g_18 != uvec2(102u, 103u)) g_result = 0;" NL
" if (g_19 != uvec3(104u, 105u, 106u)) g_result = 0;" NL
" if (g_20 != uvec4(107u, 108u, 109u, 110u)) g_result = 0;" NL "}";
m_program = CreateComputeProgram(glsl_cs);
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return ERROR;
glGenBuffers(1, &m_storage_buffer);
/* create buffer */
{
const int data = 123;
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), &data, GL_STATIC_DRAW);
}
glProgramUniform1f(m_program, glGetUniformLocation(m_program, "g_0"), 1.0f);
glProgramUniform2f(m_program, glGetUniformLocation(m_program, "g_1"), 2.0f, 3.0f);
glProgramUniform3f(m_program, glGetUniformLocation(m_program, "g_2"), 4.0f, 5.0f, 6.0f);
glProgramUniform4f(m_program, glGetUniformLocation(m_program, "g_3"), 7.0f, 8.0f, 9.0f, 10.0f);
/* mat2 */
{
const GLfloat value[4] = { 11.0f, 12.0f, 13.0f, 14.0f };
glProgramUniformMatrix2fv(m_program, glGetUniformLocation(m_program, "g_4"), 1, GL_FALSE, value);
}
/* mat2x3 */
{
const GLfloat value[6] = { 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f };
glProgramUniformMatrix2x3fv(m_program, glGetUniformLocation(m_program, "g_5"), 1, GL_FALSE, value);
}
/* mat2x4 */
{
const GLfloat value[8] = { 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, 27.0f, 28.0f };
glProgramUniformMatrix2x4fv(m_program, glGetUniformLocation(m_program, "g_6"), 1, GL_FALSE, value);
}
/* mat3x2 */
{
const GLfloat value[6] = { 29.0f, 30.0f, 31.0f, 32.0f, 33.0f, 34.0f };
glProgramUniformMatrix3x2fv(m_program, glGetUniformLocation(m_program, "g_7"), 1, GL_FALSE, value);
}
/* mat3 */
{
const GLfloat value[9] = { 35.0f, 36.0f, 37.0f, 38.0f, 39.0f, 40.0f, 41.0f, 42.0f, 43.0f };
glProgramUniformMatrix3fv(m_program, glGetUniformLocation(m_program, "g_8"), 1, GL_FALSE, value);
}
/* mat3x4 */
{
const GLfloat value[12] = { 44.0f, 45.0f, 46.0f, 47.0f, 48.0f, 49.0f,
50.0f, 51.0f, 52.0f, 53.0f, 54.0f, 55.0f };
glProgramUniformMatrix3x4fv(m_program, glGetUniformLocation(m_program, "g_9"), 1, GL_FALSE, value);
}
/* mat4x2 */
{
const GLfloat value[8] = { 56.0f, 57.0f, 58.0f, 59.0f, 60.0f, 61.0f, 62.0f, 63.0f };
glProgramUniformMatrix4x2fv(m_program, glGetUniformLocation(m_program, "g_10"), 1, GL_FALSE, value);
}
/* mat4x3 */
{
const GLfloat value[12] = {
63.0f, 64.0f, 65.0f, 66.0f, 67.0f, 68.0f, 69.0f, 70.0f, 71.0f, 27.0f, 73, 74.0f
};
glProgramUniformMatrix4x3fv(m_program, glGetUniformLocation(m_program, "g_11"), 1, GL_FALSE, value);
}
/* mat4 */
{
const GLfloat value[16] = { 75.0f, 76.0f, 77.0f, 78.0f, 79.0f, 80.0f, 81.0f, 82.0f,
83.0f, 84.0f, 85.0f, 86.0f, 87.0f, 88.0f, 89.0f, 90.0f };
glProgramUniformMatrix4fv(m_program, glGetUniformLocation(m_program, "g_12"), 1, GL_FALSE, value);
}
glProgramUniform1i(m_program, glGetUniformLocation(m_program, "g_13"), 91);
glProgramUniform2i(m_program, glGetUniformLocation(m_program, "g_14"), 92, 93);
glProgramUniform3i(m_program, glGetUniformLocation(m_program, "g_15"), 94, 95, 96);
glProgramUniform4i(m_program, glGetUniformLocation(m_program, "g_16"), 97, 98, 99, 100);
glProgramUniform1ui(m_program, glGetUniformLocation(m_program, "g_17"), 101);
glProgramUniform2ui(m_program, glGetUniformLocation(m_program, "g_18"), 102, 103);
glProgramUniform3ui(m_program, glGetUniformLocation(m_program, "g_19"), 104, 105, 106);
glProgramUniform4ui(m_program, glGetUniformLocation(m_program, "g_20"), 107, 108, 109, 110);
glUseProgram(m_program);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
{
int data;
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(data), &data);
if (data != 1)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Data is " << data << " should be 1." << tcu::TestLog::EndMessage;
return ERROR;
}
}
// re-link program (all uniforms will be set to zero)
glLinkProgram(m_program);
{
const int data = 123;
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(data), &data);
}
glUniform1f(glGetUniformLocation(m_program, "g_0"), 1.0f);
glUniform2f(glGetUniformLocation(m_program, "g_1"), 2.0f, 3.0f);
glUniform3f(glGetUniformLocation(m_program, "g_2"), 4.0f, 5.0f, 6.0f);
glUniform4f(glGetUniformLocation(m_program, "g_3"), 7.0f, 8.0f, 9.0f, 10.0f);
/* mat2 */
{
const GLfloat value[4] = { 11.0f, 12.0f, 13.0f, 14.0f };
glUniformMatrix2fv(glGetUniformLocation(m_program, "g_4"), 1, GL_FALSE, value);
}
/* mat2x3 */
{
const GLfloat value[6] = { 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f };
glUniformMatrix2x3fv(glGetUniformLocation(m_program, "g_5"), 1, GL_FALSE, value);
}
/* mat2x4 */
{
const GLfloat value[8] = { 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, 27.0f, 28.0f };
glUniformMatrix2x4fv(glGetUniformLocation(m_program, "g_6"), 1, GL_FALSE, value);
}
/* mat3x2 */
{
const GLfloat value[6] = { 29.0f, 30.0f, 31.0f, 32.0f, 33.0f, 34.0f };
glUniformMatrix3x2fv(glGetUniformLocation(m_program, "g_7"), 1, GL_FALSE, value);
}
/* mat3 */
{
const GLfloat value[9] = { 35.0f, 36.0f, 37.0f, 38.0f, 39.0f, 40.0f, 41.0f, 42.0f, 43.0f };
glUniformMatrix3fv(glGetUniformLocation(m_program, "g_8"), 1, GL_FALSE, value);
}
/* mat3x4 */
{
const GLfloat value[12] = { 44.0f, 45.0f, 46.0f, 47.0f, 48.0f, 49.0f,
50.0f, 51.0f, 52.0f, 53.0f, 54.0f, 55.0f };
glUniformMatrix3x4fv(glGetUniformLocation(m_program, "g_9"), 1, GL_FALSE, value);
}
/* mat4x2 */
{
const GLfloat value[8] = { 56.0f, 57.0f, 58.0f, 59.0f, 60.0f, 61.0f, 62.0f, 63.0f };
glUniformMatrix4x2fv(glGetUniformLocation(m_program, "g_10"), 1, GL_FALSE, value);
}
/* mat4x3 */
{
const GLfloat value[12] = {
63.0f, 64.0f, 65.0f, 66.0f, 67.0f, 68.0f, 69.0f, 70.0f, 71.0f, 27.0f, 73, 74.0f
};
glUniformMatrix4x3fv(glGetUniformLocation(m_program, "g_11"), 1, GL_FALSE, value);
}
/* mat4 */
{
const GLfloat value[16] = { 75.0f, 76.0f, 77.0f, 78.0f, 79.0f, 80.0f, 81.0f, 82.0f,
83.0f, 84.0f, 85.0f, 86.0f, 87.0f, 88.0f, 89.0f, 90.0f };
glUniformMatrix4fv(glGetUniformLocation(m_program, "g_12"), 1, GL_FALSE, value);
}
glUniform1i(glGetUniformLocation(m_program, "g_13"), 91);
glUniform2i(glGetUniformLocation(m_program, "g_14"), 92, 93);
glUniform3i(glGetUniformLocation(m_program, "g_15"), 94, 95, 96);
glUniform4i(glGetUniformLocation(m_program, "g_16"), 97, 98, 99, 100);
glUniform1ui(glGetUniformLocation(m_program, "g_17"), 101);
glUniform2ui(glGetUniformLocation(m_program, "g_18"), 102, 103);
glUniform3ui(glGetUniformLocation(m_program, "g_19"), 104, 105, 106);
glUniform4ui(glGetUniformLocation(m_program, "g_20"), 107, 108, 109, 110);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
/* validate */
{
int data;
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(data), &data);
if (data != 1)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Data is " << data << " should be 1." << tcu::TestLog::EndMessage;
return ERROR;
}
}
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_storage_buffer);
return NO_ERROR;
}
};
class BasicBuiltinVariables : public ComputeShaderBase
{
virtual std::string Title()
{
return "CS built-in variables";
}
virtual std::string Purpose()
{
return NL "Verify that all (gl_WorkGroupSize, gl_WorkGroupID, gl_LocalInvocationID," NL
"gl_GlobalInvocationID, gl_NumWorkGroups, gl_WorkGroupSize)" NL
"CS built-in variables has correct values.";
}
virtual std::string Method()
{
return NL "1. Create CS which writes all built-in variables to SSBO." NL
"2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL
"3. Verify SSBO content." NL "4. Repeat for several different local and global work sizes.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
GLuint m_program;
GLuint m_storage_buffer;
GLuint m_dispatch_buffer;
std::string GenSource(const uvec3& local_size, const uvec3& num_groups)
{
const uvec3 global_size = local_size * num_groups;
std::stringstream ss;
ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y()
<< ", local_size_z = " << local_size.z() << ") in;" NL "const uvec3 kGlobalSize = uvec3(" << global_size.x()
<< ", " << global_size.y() << ", " << global_size.z()
<< ");" NL "layout(std430) buffer OutputBuffer {" NL " uvec4 num_work_groups["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uvec4 work_group_size["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uvec4 work_group_id["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uvec4 local_invocation_id["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uvec4 global_invocation_id["
<< global_size.x() * global_size.y() * global_size.z() << "];" NL " uvec4 local_invocation_index["
<< global_size.x() * global_size.y() * global_size.z()
<< "];" NL "} g_out_buffer;" NL "void main() {" NL
" if ((gl_WorkGroupSize * gl_WorkGroupID + gl_LocalInvocationID) != gl_GlobalInvocationID) return;" NL
" const uint global_index = gl_GlobalInvocationID.x +" NL
" gl_GlobalInvocationID.y * kGlobalSize.x +" NL
" gl_GlobalInvocationID.z * kGlobalSize.x * kGlobalSize.y;" NL
" g_out_buffer.num_work_groups[global_index] = uvec4(gl_NumWorkGroups, 0);" NL
" g_out_buffer.work_group_size[global_index] = uvec4(gl_WorkGroupSize, 0);" NL
" g_out_buffer.work_group_id[global_index] = uvec4(gl_WorkGroupID, 0);" NL
" g_out_buffer.local_invocation_id[global_index] = uvec4(gl_LocalInvocationID, 0);" NL
" g_out_buffer.global_invocation_id[global_index] = uvec4(gl_GlobalInvocationID, 0);" NL
" g_out_buffer.local_invocation_index[global_index] = uvec4(gl_LocalInvocationIndex);" NL "}";
return ss.str();
}
bool RunIteration(const uvec3& local_size, const uvec3& num_groups, bool dispatch_indirect)
{
if (m_program != 0)
glDeleteProgram(m_program);
m_program = CreateComputeProgram(GenSource(local_size, num_groups));
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return false;
const GLuint kBufferSize =
local_size.x() * num_groups.x() * local_size.y() * num_groups.y() * local_size.z() * num_groups.z();
std::vector<uvec4> data(kBufferSize * 6);
if (m_storage_buffer == 0)
glGenBuffers(1, &m_storage_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uvec4) * kBufferSize * 6, &data[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
glUseProgram(m_program);
if (dispatch_indirect)
{
if (m_dispatch_buffer == 0)
glGenBuffers(1, &m_dispatch_buffer);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer);
glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_groups), &num_groups[0], GL_STATIC_DRAW);
glDispatchComputeIndirect(0);
}
else
{
glDispatchCompute(num_groups.x(), num_groups.y(), num_groups.z());
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(uvec4) * kBufferSize * 6, &data[0]);
// gl_NumWorkGroups
for (GLuint index = 0; index < kBufferSize; ++index)
{
if (!IsEqual(data[index], uvec4(num_groups.x(), num_groups.y(), num_groups.z(), 0)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "gl_NumWorkGroups: Invalid data at index " << index << "."
<< tcu::TestLog::EndMessage;
return false;
}
}
// gl_WorkGroupSize
for (GLuint index = kBufferSize; index < 2 * kBufferSize; ++index)
{
if (!IsEqual(data[index], uvec4(local_size.x(), local_size.y(), local_size.z(), 0)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "gl_WorkGroupSize: Invalid data at index " << index << "."
<< tcu::TestLog::EndMessage;
return false;
}
}
// gl_WorkGroupID
for (GLuint index = 2 * kBufferSize; index < 3 * kBufferSize; ++index)
{
uvec3 expected = IndexTo3DCoord(index - 2 * kBufferSize, local_size.x() * num_groups.x(),
local_size.y() * num_groups.y());
expected.x() /= local_size.x();
expected.y() /= local_size.y();
expected.z() /= local_size.z();
if (!IsEqual(data[index], uvec4(expected.x(), expected.y(), expected.z(), 0)))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "gl_WorkGroupID: Invalid data at index "
<< index << "." << tcu::TestLog::EndMessage;
return false;
}
}
// gl_LocalInvocationID
for (GLuint index = 3 * kBufferSize; index < 4 * kBufferSize; ++index)
{
uvec3 expected = IndexTo3DCoord(index - 3 * kBufferSize, local_size.x() * num_groups.x(),
local_size.y() * num_groups.y());
expected.x() %= local_size.x();
expected.y() %= local_size.y();
expected.z() %= local_size.z();
if (!IsEqual(data[index], uvec4(expected.x(), expected.y(), expected.z(), 0)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "gl_LocalInvocationID: Invalid data at index " << index << "."
<< tcu::TestLog::EndMessage;
return false;
}
}
// gl_GlobalInvocationID
for (GLuint index = 4 * kBufferSize; index < 5 * kBufferSize; ++index)
{
uvec3 expected = IndexTo3DCoord(index - 4 * kBufferSize, local_size.x() * num_groups.x(),
local_size.y() * num_groups.y());
if (!IsEqual(data[index], uvec4(expected.x(), expected.y(), expected.z(), 0)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "gl_GlobalInvocationID: Invalid data at index " << index << "."
<< tcu::TestLog::EndMessage;
return false;
}
}
// gl_LocalInvocationIndex
for (GLuint index = 5 * kBufferSize; index < 6 * kBufferSize; ++index)
{
uvec3 coord = IndexTo3DCoord(index - 5 * kBufferSize, local_size.x() * num_groups.x(),
local_size.y() * num_groups.y());
const GLuint expected = (coord.x() % local_size.x()) + (coord.y() % local_size.y()) * local_size.x() +
(coord.z() % local_size.z()) * local_size.x() * local_size.y();
if (!IsEqual(data[index], uvec4(expected)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "gl_LocalInvocationIndex: Invalid data at index " << index << "."
<< tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
virtual long Setup()
{
m_program = 0;
m_storage_buffer = 0;
m_dispatch_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
if (!RunIteration(uvec3(64, 1, 1), uvec3(8, 1, 1), false))
return ERROR;
if (!RunIteration(uvec3(1, 1, 64), uvec3(1, 5, 2), true))
return ERROR;
if (!RunIteration(uvec3(1, 1, 4), uvec3(2, 2, 2), false))
return ERROR;
if (!RunIteration(uvec3(3, 2, 1), uvec3(1, 2, 3), true))
return ERROR;
if (!RunIteration(uvec3(2, 4, 2), uvec3(2, 4, 1), false))
return ERROR;
if (!RunIteration(uvec3(2, 4, 7), uvec3(2, 1, 4), true))
return ERROR;
return NO_ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_storage_buffer);
glDeleteBuffers(1, &m_dispatch_buffer);
return NO_ERROR;
}
};
class BasicMax : public ComputeShaderBase
{
virtual std::string Title()
{
return NL "CS max values";
}
virtual std::string Purpose()
{
return NL "Verify (on the API and GLSL side) that all GL_MAX_COMPUTE_* values are not less than" NL
"required by the OpenGL specification.";
}
virtual std::string Method()
{
return NL "1. Use all API commands to query all GL_MAX_COMPUTE_* values. Verify that they are correct." NL
"2. Verify all gl_MaxCompute* constants in the GLSL.";
}
virtual std::string PassCriteria()
{
return NL "Everything works as expected.";
}
GLuint m_program;
GLuint m_buffer;
bool CheckIndexed(GLenum target, const GLint* min_values)
{
GLint i;
GLint64 i64;
GLfloat f;
GLdouble d;
GLboolean b;
for (GLuint c = 0; c < 3; c++)
{
glGetIntegeri_v(target, c, &i);
if (i < min_values[c])
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << i << " should be at least "
<< min_values[c] << "." << tcu::TestLog::EndMessage;
return false;
}
}
for (GLuint c = 0; c < 3; c++)
{
glGetInteger64i_v(target, c, &i64);
if (i64 < static_cast<GLint64>(min_values[c]))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Is " << i64 << " should be at least "
<< static_cast<GLint64>(min_values[c]) << "." << tcu::TestLog::EndMessage;
return false;
}
}
for (GLuint c = 0; c < 3; c++)
{
glGetFloati_v(target, c, &f);
if (f < static_cast<GLfloat>(min_values[c]))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Is " << f << " should be at least "
<< static_cast<GLfloat>(min_values[c]) << "." << tcu::TestLog::EndMessage;
return false;
}
}
for (GLuint c = 0; c < 3; c++)
{
glGetDoublei_v(target, c, &d);
if (d < static_cast<GLdouble>(min_values[c]))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Is " << d << " should be at least "
<< static_cast<GLdouble>(min_values[c]) << "." << tcu::TestLog::EndMessage;
return false;
}
}
for (GLuint c = 0; c < 3; c++)
{
glGetBooleani_v(target, c, &b);
if (b != (min_values[c] ? GL_TRUE : GL_FALSE))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Is " << b << " should be " << (min_values[c] ? GL_TRUE : GL_FALSE)
<< "." << tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
bool Check(GLenum target, const GLint min_value)
{
GLint i;
GLint64 i64;
GLfloat f;
GLdouble d;
GLboolean b;
glGetIntegerv(target, &i);
if (i < min_value)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << i << " should be at least "
<< min_value << "." << tcu::TestLog::EndMessage;
return false;
}
glGetInteger64v(target, &i64);
if (i64 < static_cast<GLint64>(min_value))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << i64 << " should be at least "
<< static_cast<GLint64>(min_value) << "." << tcu::TestLog::EndMessage;
return false;
}
glGetFloatv(target, &f);
if (f < static_cast<GLfloat>(min_value))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << f << " should be at least "
<< static_cast<GLfloat>(min_value) << "." << tcu::TestLog::EndMessage;
return false;
}
glGetDoublev(target, &d);
if (d < static_cast<GLdouble>(min_value))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << d << " should be at least "
<< static_cast<GLdouble>(min_value) << "." << tcu::TestLog::EndMessage;
return false;
}
glGetBooleanv(target, &b);
if (b != (min_value ? GL_TRUE : GL_FALSE))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << b << " should be "
<< (min_value ? GL_TRUE : GL_FALSE) << "." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
virtual long Setup()
{
m_program = 0;
m_buffer = 0;
return NO_ERROR;
}
virtual long Run()
{
const GLint work_group_count[3] = { 65535, 65535, 65535 };
if (!CheckIndexed(GL_MAX_COMPUTE_WORK_GROUP_COUNT, work_group_count))
return ERROR;
const GLint work_group_size[3] = { 1024, 1024, 64 };
if (!CheckIndexed(GL_MAX_COMPUTE_WORK_GROUP_SIZE, work_group_size))
return ERROR;
if (!Check(GL_MAX_COMPUTE_UNIFORM_BLOCKS, 12))
return ERROR;
if (!Check(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, 16))
return ERROR;
if (!Check(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, 8))
return ERROR;
if (!Check(GL_MAX_COMPUTE_ATOMIC_COUNTERS, 8))
return ERROR;
if (!Check(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, 32768))
return ERROR;
if (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
{
if (!Check(GL_MAX_COMPUTE_UNIFORM_COMPONENTS, 1024))
return ERROR;
}
else
{
if (!Check(GL_MAX_COMPUTE_UNIFORM_COMPONENTS, 512))
return ERROR;
}
if (!Check(GL_MAX_COMPUTE_IMAGE_UNIFORMS, 8))
return ERROR;
if (!Check(GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS, 512))
return ERROR;
const char* const glsl_cs =
NL "layout(local_size_x = 1) in;" NL "layout(std430) buffer Output {" NL " int g_output;" NL "};" NL
"uniform ivec3 MaxComputeWorkGroupCount;" NL "uniform ivec3 MaxComputeWorkGroupSize;" NL
"uniform int MaxComputeUniformComponents;" NL "uniform int MaxComputeTextureImageUnits;" NL
"uniform int MaxComputeImageUniforms;" NL "uniform int MaxComputeAtomicCounters;" NL
"uniform int MaxComputeAtomicCounterBuffers;" NL "void main() {" NL " g_output = 1;" NL
" if (MaxComputeWorkGroupCount != gl_MaxComputeWorkGroupCount) g_output = 0;" NL
" if (MaxComputeWorkGroupSize != gl_MaxComputeWorkGroupSize) g_output = 0;" NL
" if (MaxComputeUniformComponents != gl_MaxComputeUniformComponents) g_output = 0;" NL
" if (MaxComputeTextureImageUnits != gl_MaxComputeTextureImageUnits) g_output = 0;" NL
" if (MaxComputeImageUniforms != gl_MaxComputeImageUniforms) g_output = 0;" NL
" if (MaxComputeAtomicCounters != gl_MaxComputeAtomicCounters) g_output = 0;" NL
" if (MaxComputeAtomicCounterBuffers != gl_MaxComputeAtomicCounterBuffers) g_output = 0;" NL "}";
m_program = CreateComputeProgram(glsl_cs);
glLinkProgram(m_program);
if (!CheckProgram(m_program))
return ERROR;
glUseProgram(m_program);
GLint p[3];
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &p[0]);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &p[1]);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &p[2]);
glUniform3i(glGetUniformLocation(m_program, "MaxComputeWorkGroupCount"), p[0], p[1], p[2]);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &p[0]);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &p[1]);
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &p[2]);
glUniform3iv(glGetUniformLocation(m_program, "MaxComputeWorkGroupSize"), 1, p);
glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_COMPONENTS, p);
glUniform1i(glGetUniformLocation(m_program, "MaxComputeUniformComponents"), p[0]);
glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, p);
glUniform1iv(glGetUniformLocation(m_program, "MaxComputeTextureImageUnits"), 1, p);
glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, p);
glUniform1i(glGetUniformLocation(m_program, "MaxComputeImageUniforms"), p[0]);
glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTERS, p);
glUniform1i(glGetUniformLocation(m_program, "MaxComputeAtomicCounters"), p[0]);
glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, p);
glUniform1i(glGetUniformLocation(m_program, "MaxComputeAtomicCounterBuffers"), p[0]);
GLint data = 0xffff;
glGenBuffers(1, &m_buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLint), &data, GL_DYNAMIC_DRAW);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLint), &data);
return data == 1 ? NO_ERROR : ERROR;
}
virtual long Cleanup()
{
glUseProgram(0);
glDeleteProgram(m_program);
glDeleteBuffers(1, &m_buffer);
return NO_ERROR;
}
};
class BasicBuildMonolithic : public ComputeShaderBase
{
virtual std::string Title()
{
return "Building CS monolithic program";
}
virtual std::string Purpose()
{
return NL "1. Verify that building monolithic CS program works as expected." NL
"2. Verify that program consisting from 3 compilation units links as expected." NL
"3. Verify that CS consisting from 2 strings compiles as expected.";
}
virtual std::string Method()
{
return NL "1. Create, compile and link CS using CreateShader, CompileShader and LinkProgram commands." NL
"2. Dispatch and verify CS program.";
}
virtual std::string PassCriteria()
{
return "Everything works as expected.";
}
virtual long Run()
{
const char* const cs1[2] = { "#version 430 core",
NL "layout(local_size_x = 1) in;" NL "void Run();" NL "void main() {" NL
" Run();" NL "}" };
const char* const cs2 =
"#version 430 core" NL "layout(binding = 0, std430) buffer Output {" NL " vec4 g_output;" NL "};" NL
"vec4 CalculateOutput();" NL "void Run() {" NL " g_output = CalculateOutput();" NL "}";
const char* const cs3 =
"#version 430 core" NL "layout(local_size_x = 1) in;" NL "layout(binding = 0, std430) buffer Output {" NL
" vec4 g_output;" NL "};" NL "vec4 CalculateOutput() {" NL " g_output = vec4(0);" NL
" return vec4(1, 2, 3, 4);" NL "}";
const GLuint sh1 = glCreateShader(GL_COMPUTE_SHADER);
GLint type;
glGetShaderiv(sh1, GL_SHADER_TYPE, &type);
if (static_cast<GLenum>(type) != GL_COMPUTE_SHADER)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "SHADER_TYPE should be COMPUTE_SHADER." << tcu::TestLog::EndMessage;
glDeleteShader(sh1);
return false;
}
glShaderSource(sh1, 2, cs1, NULL);
glCompileShader(sh1);
const GLuint sh2 = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(sh2, 1, &cs2, NULL);
glCompileShader(sh2);
const GLuint sh3 = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(sh3, 1, &cs3, NULL);
glCompileShader(sh3);
const GLuint p = glCreateProgram();
glAttachShader(p, sh1);
glAttachShader(p, sh2);
glAttachShader(p, sh3);
glLinkProgram(p);
glDeleteShader(sh1);
glDeleteShader(sh2);
glDeleteShader(sh3);
bool res = CheckProgram(p);
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4), &vec4(0.0f)[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
glUseProgram(p);
glDispatchCompute(1, 1, 1);
vec4 data;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4), &data[0]);
if (!IsEqual(data, vec4(1.0f, 2.0f, 3.0f, 4.0f)))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Invalid value!" << tcu::TestLog::EndMessage;
res = false;
}
glDeleteBuffers(1, &buffer);
glUseProgram(0);
glDeleteProgram(p);
return res == true ? NO_ERROR : ERROR;
}
};
class BasicBuildSeparable : public ComputeShaderBase
{
virtual std::string Title()
{
return "Building CS separable program";
}
virtual std::string Purpose()
{
return NL "1. Verify that building separable CS program works as expected." NL
"2. Verify that program consisting from 4 strings works as expected.";
}
virtual std::string Method()
{
return NL "1. Create, compile and link CS using CreateShaderProgramv command." NL
"2. Dispatch and verify CS program.";