| /*------------------------------------------------------------------------- |
| * 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."; |
|