| /*------------------------------------------------------------------------- |
| * 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 "es31cComputeShaderTests.hpp" |
| #include "gluContextInfo.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuMatrix.hpp" |
| #include "tcuMatrixUtil.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include <cstdarg> |
| #include <sstream> |
| |
| namespace glcts |
| { |
| |
| 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 310 es\n"; |
| |
| class ComputeShaderBase : public glcts::SubcaseBase |
| { |
| |
| public: |
| virtual ~ComputeShaderBase() |
| { |
| } |
| |
| ComputeShaderBase() |
| : renderTarget(m_context.getRenderContext().getRenderTarget()), pixelFormat(renderTarget.getPixelFormat()) |
| { |
| g_color_eps = vec4(1.f / (1 << 13)); |
| if (pixelFormat.redBits != 0) |
| { |
| g_color_eps.x() += 1.f / (static_cast<float>(1 << pixelFormat.redBits) - 1.0f); |
| } |
| if (pixelFormat.greenBits != 0) |
| { |
| g_color_eps.y() += 1.f / (static_cast<float>(1 << pixelFormat.greenBits) - 1.0f); |
| } |
| if (pixelFormat.blueBits != 0) |
| { |
| g_color_eps.z() += 1.f / (static_cast<float>(1 << pixelFormat.blueBits) - 1.0f); |
| } |
| if (pixelFormat.alphaBits != 0) |
| { |
| g_color_eps.w() += 1.f / (static_cast<float>(1 << pixelFormat.alphaBits) - 1.0f); |
| } |
| } |
| |
| 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; |
| glGetProgramiv(program, GL_LINK_STATUS, &status); |
| |
| if (status == GL_FALSE) |
| { |
| GLint attached_shaders = 0; |
| 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_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 = 0; |
| 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); |
| std::vector<GLubyte> data(w * h * 4); |
| glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| |
| for (int i = 0; i < w * h * 4; i += 4) |
| { |
| display[i / 4] = vec4(static_cast<GLfloat>(data[i] / 255.), static_cast<GLfloat>(data[i + 1] / 255.), |
| static_cast<GLfloat>(data[i + 2] / 255.), static_cast<GLfloat>(data[i + 3] / 255.)); |
| } |
| |
| 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); |
| std::vector<GLubyte> data(width * height * 4); |
| glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| |
| for (int i = 0; i < width * height * 4; i += 4) |
| { |
| fb[i / 4] = vec3(static_cast<GLfloat>(data[i] / 255.), static_cast<GLfloat>(data[i + 1] / 255.), |
| static_cast<GLfloat>(data[i + 2] / 255.)); |
| } |
| |
| int startx = int((static_cast<float>(width) * 0.1f) + 1); |
| int starty = int((static_cast<float>(height) * 0.1f) + 1); |
| int endx = int(static_cast<float>(width) - 2 * ((static_cast<float>(width) * 0.1f) + 1) - 1); |
| int endy = int(static_cast<float>(height) - 2 * ((static_cast<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); |
| std::vector<GLubyte> data(width * height * 4); |
| glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| |
| for (int i = 0; i < width * height * 4; i += 4) |
| { |
| fb[i / 4] = vec3(static_cast<GLfloat>(data[i] / 255.), static_cast<GLfloat>(data[i + 1] / 255.), |
| static_cast<GLfloat>(data[i + 2] / 255.)); |
| } |
| |
| 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(128, 1, 1, 3, true)) |
| return ERROR; |
| if (!RunIteration(2, 8, 8, 3, false)) |
| return ERROR; |
| if (!RunIteration(2, 2, 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 " 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); |
| } |
| |
| 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) * static_cast<float>(kBufferSize) + static_cast<float>(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()); |
| } |
| |
| vec4* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| data = static_cast<vec4*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * 12 * kBufferSize, GL_MAP_READ_BIT)); |
| |
| bool ret = true; |
| 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 << "Invalid data at offset " |
| << index * 12 + i << 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; |
| 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 (sampler2D, sampler3D," NL " sampler2DArray)." 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[3]; |
| 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 sampler2D g_sampler0;" NL "uniform lowp sampler3D g_sampler1;" NL |
| "uniform mediump sampler2DArray g_sampler2;" 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 "} g_out_buffer;" NL "void main() {" NL " 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] = texture(g_sampler0, vec2(gl_GlobalInvocationID) / " |
| "vec2(kGlobalSize));" NL " g_out_buffer.data1[global_index] = textureProj(g_sampler1, " |
| "vec4(vec3(gl_GlobalInvocationID) / vec3(kGlobalSize), 1.0));" NL |
| " g_out_buffer.data2[global_index] = texelFetchOffset(g_sampler2, ivec3(gl_GlobalInvocationID), 0, " |
| "ivec2(0));" 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 < 4; ++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 * 4); |
| 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 * 4, &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(3, m_texture); |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, m_texture[0]); |
| 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_TEXTURE1); |
| glBindTexture(GL_TEXTURE_3D, m_texture[1]); |
| 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_TEXTURE2); |
| glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture[2]); |
| 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]); |
| |
| 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()); |
| } |
| |
| vec4* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| data = static_cast<vec4*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * 3 * kBufferSize, GL_MAP_READ_BIT)); |
| bool ret = true; |
| for (GLuint index = 0; index < kBufferSize * 3; ++index) |
| { |
| if (!IsEqual(data[index], vec4(123.0f))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at index " << 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; |
| memset(m_texture, 0, sizeof(m_texture)); |
| 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(3, m_texture); |
| 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; |
| if (m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic")) |
| { |
| ss << NL "#extension GL_OES_shader_image_atomic : enable"; |
| } |
| ss << NL "layout(local_size_x = " << local_size.x() << ", local_size_y = " << local_size.y() |
| << ", local_size_z = " << local_size.z() |
| << ") in;" NL "layout(r32ui, binding=0) coherent uniform mediump uimage2D g_image1;" NL |
| "layout(r32ui, binding=1) uniform mediump uimage2D 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 |
| " uvec4 color = uvec4(gl_GlobalInvocationID.x + gl_GlobalInvocationID.y);"; |
| if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic")) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Function imageAtomicAdd not supported, using imageStore" |
| << tcu::TestLog::EndMessage; |
| ss << NL " imageStore(g_image1, ivec2(gl_GlobalInvocationID), color);" NL |
| " uvec4 c = imageLoad(g_image1, ivec2(gl_GlobalInvocationID));" NL |
| " imageStore(g_image2, ivec2(gl_GlobalInvocationID), c);" NL "}"; |
| } |
| else |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Using imageAtomicAdd" << tcu::TestLog::EndMessage; |
| ss << NL " imageStore(g_image1, ivec2(gl_GlobalInvocationID), uvec4(0));" NL |
| " imageAtomicAdd(g_image1, ivec2(gl_GlobalInvocationID), color.x);" NL |
| " uvec4 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; |
| |
| 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<uvec4> data(kSize); |
| 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_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, kWidth, kHeight); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, &data[0]); |
| } |
| glBindTexture(GL_TEXTURE_2D, 0); |
| |
| glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); |
| glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI); |
| 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); |
| std::vector<GLubyte> colorData(kWidth * kHeight * 4); |
| glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, &colorData[0]); |
| glDeleteTextures(2, m_texture); |
| |
| for (int i = 0; i < kWidth * kHeight * 4; i += 4) |
| { |
| display[i / 4] = |
| vec4(static_cast<GLfloat>(colorData[i] / 255.), static_cast<GLfloat>(colorData[i + 1] / 255.), |
| static_cast<GLfloat>(colorData[i + 2] / 255.), static_cast<GLfloat>(colorData[i + 3] / 255.)); |
| } |
| |
| /* As the colors are converted R8->Rx and then read back as Rx->R8, |
| need to add both conversions to the epsilon. */ |
| vec4 kColorEps = g_color_eps; |
| kColorEps.x() += 1.f / ((1 << 8) - 1.0f); |
| 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, 1.0f, 1.0f, 1.0f); |
| if (!ColorEqual(display[y * kWidth + x], c, kColorEps)) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Got red: " << display[y * kWidth + x].x() << ", expected " << c.x() |
| << ", 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 "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 "}"; |
| |
| const char* glsl_fs = |
| NL "layout(location = 0) out mediump vec4 o_color;" NL "uniform mediump usampler2D g_image1;" NL |
| "uniform mediump usampler2D g_image2;" NL "void main() {" NL |
| " mediump uvec4 c1 = texelFetch(g_image1, ivec2(gl_FragCoord.xy), 0);" NL |
| " mediump uvec4 c2 = texelFetch(g_image2, ivec2(gl_FragCoord.xy), 0);" NL |
| " if (c1 == c2) o_color = vec4(float(c1.x)/255.0, 1.0, 1.0, 1.0);" 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 (!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."; |
| } |
| |
| 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; |
| 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 = 0, offset = 4) uniform atomic_uint g_dec_counter;" NL "void main() {" NL |
| " 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; |
| |
| 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) |
| glGenBuffers(1, &m_counter_buffer); |
| |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_counter_buffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, 2 * sizeof(GLuint), NULL, GL_STREAM_DRAW); |
| *static_cast<GLuint*>(glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), GL_MAP_WRITE_BIT)) = 0; |
| glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER); |
| *static_cast<GLuint*>( |
| glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), sizeof(GLuint), GL_MAP_WRITE_BIT)) = 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()); |
| } |
| |
| GLuint* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| data = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * kSize, GL_MAP_READ_BIT)); |
| |
| bool ret = true; |
| 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; |
| ret = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| GLuint* value; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_counter_buffer); |
| value = |
| static_cast<GLuint*>(glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, 2 * sizeof(GLuint), GL_MAP_READ_BIT)); |
| if (value[0] != kSize) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Final atomic counter value (buffer 0, offset 0) is " << value[0] |
| << " should be " << kSize << tcu::TestLog::EndMessage; |
| ret = false; |
| } |
| if (value[1] != 0) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Final atomic counter value (buffer 0, offset 4) is " << value[1] |
| << " should be 0" << tcu::TestLog::EndMessage; |
| ret = false; |
| } |
| glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER); |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0); |
| |
| return ret; |
| } |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer = 0; |
| m_counter_buffer = 0; |
| 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(1, &m_counter_buffer); |
| glDeleteBuffers(1, &m_dispatch_buffer); |
| glDeleteBuffers(1, &m_storage_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* commands."; |
| } |
| |
| 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 Uniform* commands." NL |
| "3. 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); |
| glUseProgram(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); |
| } |
| |
| 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); |
| |
| long error = NO_ERROR; |
| /* validate */ |
| { |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), GL_MAP_READ_BIT)); |
| if (data[0] != 1) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is " << data[0] << " should be 1." << tcu::TestLog::EndMessage; |
| 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_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 |
| " 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); |
| uvec4* result; |
| result = static_cast<uvec4*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(uvec4) * kBufferSize * 6, GL_MAP_READ_BIT)); |
| |
| // gl_NumWorkGroups |
| for (GLuint index = 0; index < kBufferSize; ++index) |
| { |
| if (!IsEqual(result[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(result[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(result[index], uvec4(expected.x(), expected.y(), expected.z(), 0))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "gl_WorkGroupSize: 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(result[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(result[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(result[index], uvec4(expected))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "gl_LocalInvocationIndex: Invalid data at index " << index |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| 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; |
| |
| 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 (static_cast<GLint>(i64) < min_values[c]) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Is " << static_cast<GLint>(i64) << " should be at least " |
| << min_values[c] << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Check(GLenum target, const GLint min_value) |
| { |
| GLint i; |
| GLint64 i64; |
| GLfloat f; |
| 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 (static_cast<GLint>(i64) < min_value) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << static_cast<GLint>(i64) |
| << " should be at least " << min_value << tcu::TestLog::EndMessage; |
| return false; |
| } |
| glGetFloatv(target, &f); |
| if (static_cast<GLint>(f) < min_value) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is " << static_cast<GLint>(f) |
| << " should be at least " << min_value << tcu::TestLog::EndMessage; |
| return false; |
| } |
| glGetBooleanv(target, &b); |
| if (b == GL_FALSE) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Is GL_FALSE should be at least GL_TRUE." |
| << min_value << 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] = { 128, 128, 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, 1)) |
| return ERROR; |
| if (!Check(GL_MAX_COMPUTE_ATOMIC_COUNTERS, 8)) |
| return ERROR; |
| if (!Check(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, 16384)) |
| return ERROR; |
| if (!Check(GL_MAX_COMPUTE_UNIFORM_COMPONENTS, 512)) |
| return ERROR; |
| if (!Check(GL_MAX_COMPUTE_IMAGE_UNIFORMS, 4)) |
| 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); |
| |
| GLint* result; |
| result = static_cast<GLint*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLint), GL_MAP_READ_BIT)); |
| long error = NO_ERROR; |
| if (result[0] != 1) |
| { |
| 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 NegativeAttachShader : public ComputeShaderBase |
| { |
| |
| virtual std::string Title() |
| { |
| return "Api Attach Shader"; |
| } |
| |
| virtual std::string Purpose() |
| { |
| return NL "Verify that calling AttachShader with multiple shader objects of type COMPUTE_SHADER generates " |
| "INVALID_OPERATION."; |
| } |
| |
| virtual std::string Method() |
| { |
| return NL "Try to attach multiple shader objects of the same type and verify that proper error is generated."; |
| } |
| |
| virtual std::string PassCriteria() |
| { |
| return "INVALID_OPERATION is generated."; |
| } |
| |
| virtual long Run() |
| { |
| const char* const cs1[2] = { "#version 310 es", NL "layout(local_size_x = 1) in;" NL "void Run();" NL |
| "void main() {" NL " Run();" NL "}" }; |
| |
| const char* const cs2 = |
| "#version 310 es" 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 310 es" 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); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GL_INVALID_OPERATION error expected after attaching shader of the same type." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| glAttachShader(p, sh3); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "GL_INVALID_OPERATION error expected after attaching shader of the same type." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glDeleteShader(sh1); |
| glDeleteShader(sh2); |
| glDeleteShader(sh3); |
| |
| glUseProgram(0); |
| glDeleteProgram(p); |
| |
| return NO_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."; |
| } |
| |
| virtual std::string PassCriteria() |
| { |
| return "Everything works as expected."; |
| } |
| |
| virtual long Run() |
| { |
| const char* const cs[4] = { |
| "#version 310 es", |
| |
| NL "layout(local_size_x = 1) in;" NL "void Run();" NL "void main() {" NL " Run();" NL "}", |
| |
| NL "layout(binding = 0, std430) buffer Output {" NL " vec4 g_output;" NL "};" NL |
| "vec4 CalculateOutput();" NL "void Run() {" NL " g_output = CalculateOutput();" NL "}", |
| |
| NL "vec4 CalculateOutput() {" NL " g_output = vec4(0);" NL " return vec4(1, 2, 3, 4);" NL "}" |
| }; |
| |
| const GLuint p = glCreateShaderProgramv(GL_COMPUTE_SHADER, 4, cs); |
| 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); |
| data = static_cast<vec4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4), GL_MAP_READ_BIT)); |
| if (!IsEqual(data[0], vec4(1.0f, 2.0f, 3.0f, 4.0f))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid value!" << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| |
| glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4), &vec4(0.0f)[0]); |
| |
| GLuint pipeline; |
| glGenProgramPipelines(1, &pipeline); |
| glUseProgramStages(pipeline, GL_COMPUTE_SHADER_BIT, p); |
| |
| glUseProgram(0); |
| glBindProgramPipeline(pipeline); |
| glDispatchCompute(1, 1, 1); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| data = static_cast<vec4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4), GL_MAP_READ_BIT)); |
| |
| if (!IsEqual(data[0], vec4(1.0f, 2.0f, 3.0f, 4.0f))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid value!" << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| glDeleteProgramPipelines(1, &pipeline); |
| glDeleteBuffers(1, &buffer); |
| glDeleteProgram(p); |
| |
| return res == true ? NO_ERROR : ERROR; |
| } |
| }; |
| |
| class BasicSharedSimple : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return "Shared Memory - simple usage"; |
| } |
| |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that shared array of uints works as expected." NL |
| "2. Verify that shared memory written by one invocation is observable by other invocations" NL |
| " when groupMemoryBarrier() and barrier() built-in functions are used."; |
| } |
| |
| virtual std::string Method() |
| { |
| return NL "1. Create and dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL |
| "2. Verify results written by CS to SSBO." NL |
| "3. Repeat for several different number of work groups."; |
| } |
| |
| virtual std::string PassCriteria() |
| { |
| return "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer; |
| GLuint m_dispatch_buffer; |
| |
| bool RunIteration(const GLuint num_groups, bool dispatch_indirect) |
| { |
| const GLuint kBufferSize = 128 * num_groups; |
| |
| std::vector<GLuint> data(kBufferSize, 0xffff); |
| 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) * kBufferSize, &data[0], GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| glUseProgram(m_program); |
| if (dispatch_indirect) |
| { |
| const GLuint groups[3] = { num_groups, 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(groups), groups, GL_STATIC_DRAW); |
| glDispatchComputeIndirect(0); |
| } |
| else |
| { |
| glDispatchCompute(num_groups, 1, 1); |
| } |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| GLuint* result; |
| result = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * kBufferSize, GL_MAP_READ_BIT)); |
| bool res = true; |
| for (GLuint i = 0; i < kBufferSize; ++i) |
| { |
| if (result[i] != 1) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << result[i] << " should be 1." << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return res; |
| } |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer = 0; |
| m_dispatch_buffer = 0; |
| return NO_ERROR; |
| } |
| |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 128) in;" NL "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL |
| "shared uint g_shared_data[128];" NL "void main() {" NL |
| " g_shared_data[gl_LocalInvocationID.x] = gl_LocalInvocationIndex;" NL |
| " groupMemoryBarrier();" // flush memory stores |
| NL " barrier();" // wait for all stores to finish |
| NL " g_output[gl_GlobalInvocationID.x] = 1u;" NL " if (gl_LocalInvocationIndex < 127u) {" NL |
| " uint res = g_shared_data[gl_LocalInvocationID.x + " |
| "1u];" // load data from shared memory filled by other thread |
| NL " if (res != (gl_LocalInvocationIndex + 1u)) {" NL " g_output[gl_GlobalInvocationID.x] = 0u;" NL |
| " }" NL " }" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| if (!RunIteration(1, false)) |
| return ERROR; |
| if (!RunIteration(8, true)) |
| return ERROR; |
| if (!RunIteration(13, false)) |
| return ERROR; |
| if (!RunIteration(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 BasicSharedStruct : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return "Shared Memory - arrays and structers"; |
| } |
| |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that vectors, matrices, structers and arrays of those can be used" NL |
| " as a shared memory." NL |
| "2. Verify that shared memory can be indexed with constant values, built-in" NL |
| " variables and dynamic expressions." NL |
| "3. Verify that memoryBarrierAtomicCounter(), memoryBarrierImage(), memoryBarrier()," NL |
| " memoryBarrierBuffer() and memoryBarrierShared() built-in functions are accepted" NL |
| " by the GLSL compiler."; |
| } |
| |
| virtual std::string Method() |
| { |
| return NL "1. Create and dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL |
| "2. Verify results written by CS to SSBO."; |
| } |
| |
| virtual std::string PassCriteria() |
| { |
| return "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer; |
| GLuint m_dispatch_buffer; |
| |
| bool RunIteration(bool dispatch_indirect) |
| { |
| const GLuint kBufferSize = 256; |
| |
| std::vector<vec4> data(kBufferSize); |
| 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, &data[0], GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| glUseProgram(m_program); |
| if (dispatch_indirect) |
| { |
| const GLuint 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(groups), groups, GL_STATIC_DRAW); |
| glDispatchComputeIndirect(0); |
| } |
| else |
| { |
| glDispatchCompute(1, 1, 1); |
| } |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| vec4* result; |
| result = static_cast<vec4*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * kBufferSize, GL_MAP_READ_BIT)); |
| bool res = true; |
| for (GLuint i = 0; i < kBufferSize; ++i) |
| { |
| if (!IsEqual(result[i], vec4(static_cast<float>(i)))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at index " << i << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return res; |
| } |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer = 0; |
| m_dispatch_buffer = 0; |
| return NO_ERROR; |
| } |
| |
| virtual long Run() |
| { |
| const char* const glsl_cs = NL |
| "layout(local_size_x = 128) in;" NL "layout(std430) buffer Output {" NL " vec4 g_output[256];" NL "};" NL |
| "struct SubData {" NL " mat2x4 data;" NL "};" NL "struct Data {" NL " vec3 data0;" NL " uint index;" NL |
| " SubData data1;" NL "};" NL "shared Data g_shared_data[256];" NL "shared int g_shared_buf[2];" NL |
| "void main() {" NL " if (gl_LocalInvocationID.x == 0u) {" NL " g_shared_buf[1] = 1;" NL |
| " g_shared_buf[1u + gl_LocalInvocationID.x] = 0;" NL " g_shared_buf[0] = 128;" NL |
| " g_output[0] = vec4(g_shared_buf[1]);" NL " g_output[128] = vec4(g_shared_buf[0]);" NL |
| " memoryBarrierBuffer();" // note: this call is not needed here, just check if compiler accepts it |
| NL " } else {" NL " uint index = gl_LocalInvocationIndex;" NL |
| " g_shared_data[index].index = index;" NL " g_shared_data[index + 128u].index = index + 128u;" NL |
| " g_shared_data[index].data1.data = mat2x4(0.0);" NL |
| " g_shared_data[index + 128u].data1.data = mat2x4(0.0);" NL |
| " g_output[index] = vec4(g_shared_data[index].index);" // load data from shared memory |
| NL " g_output[index + 128u] = vec4(g_shared_data[index + 128u].index);" NL |
| " memoryBarrierShared();" // note: this call is not needed here, just check if compiler accepts it |
| NL " }" NL " memoryBarrierAtomicCounter();" NL " memoryBarrierImage();" NL |
| " memoryBarrier();" // note: these calls are not needed here, just check if compiler accepts them |
| NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| if (!RunIteration(false)) |
| return ERROR; |
| if (!RunIteration(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 BasicDispatchIndirect : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "DispatchComputeIndirect command"; |
| } |
| |
| virtual std::string Purpose() |
| { |
| return NL |
| "1. Verify that DispatchComputeIndirect command works as described in the OpenGL specification." NL |
| "2. Verify that <offset> parameter is correctly applied." NL |
| "3. Verify that updating dispatch buffer with different methods (BufferData, BufferSubData, MapBuffer)" NL |
| " just before DispatchComputeIndirect call works as expected." NL |
| "4. Verify that GL_DISPATCH_INDIRECT_BUFFER_BINDING binding point is set correctly."; |
| } |
| |
| virtual std::string Method() |
| { |
| return NL |
| "1. Create CS and dispatch indirect buffer." NL "2. Dispatch CS with DispatchComputeIndirect command." NL |
| "3. Update dispatch indirect buffer." NL |
| "4. Repeat several times updating dispatch buffer with different methods and changing <offset> parameter."; |
| } |
| |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer; |
| GLuint m_dispatch_buffer[2]; |
| |
| bool RunIteration(GLintptr offset, GLuint buffer_size) |
| { |
| std::vector<GLuint> data(buffer_size); |
| 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) * buffer_size, &data[0], GL_DYNAMIC_DRAW); |
| |
| glDispatchComputeIndirect(offset); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| GLuint* result; |
| result = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * buffer_size, GL_MAP_READ_BIT)); |
| bool res = true; |
| for (GLuint i = 0; i < buffer_size; ++i) |
| { |
| if (result[i] != i) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << result[i] << " should be " << i << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return res; |
| } |
| |
| bool CheckBinding(GLuint expected) |
| { |
| GLint i; |
| GLint64 i64; |
| GLfloat f; |
| GLboolean b; |
| |
| GLfloat expectedFloat = static_cast<GLfloat>(expected); |
| |
| glGetIntegerv(GL_DISPATCH_INDIRECT_BUFFER_BINDING, &i); |
| if (static_cast<GLuint>(i) != expected) |
| { |
| return false; |
| } |
| glGetInteger64v(GL_DISPATCH_INDIRECT_BUFFER_BINDING, &i64); |
| if (static_cast<GLuint>(i64) != expected) |
| { |
| return false; |
| } |
| glGetFloatv(GL_DISPATCH_INDIRECT_BUFFER_BINDING, &f); |
| if (f != expectedFloat) |
| { |
| return false; |
| } |
| glGetBooleanv(GL_DISPATCH_INDIRECT_BUFFER_BINDING, &b); |
| if (b != (expected != 0 ? GL_TRUE : GL_FALSE)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer = 0; |
| memset(m_dispatch_buffer, 0, sizeof(m_dispatch_buffer)); |
| return NO_ERROR; |
| } |
| |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 1) in;" NL "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL |
| "uniform uvec3 g_global_size;" NL "void main() {" NL " uint global_index = gl_GlobalInvocationID.x +" NL |
| " gl_GlobalInvocationID.y * g_global_size.x +" NL |
| " gl_GlobalInvocationID.z * g_global_size.x * g_global_size.y;" NL |
| " if (gl_NumWorkGroups != g_global_size) {" NL " g_output[global_index] = 0xffffu;" NL |
| " return;" NL " }" NL " g_output[global_index] = global_index;" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| if (!CheckBinding(0)) |
| return ERROR; |
| |
| glGenBuffers(2, m_dispatch_buffer); |
| |
| const GLuint data[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; |
| const GLuint data2[] = { 3, 1, 4, 4 }; |
| |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer[0]); |
| glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(data), data, GL_STREAM_DRAW); |
| if (!CheckBinding(m_dispatch_buffer[0])) |
| return ERROR; |
| |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer[1]); |
| glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(data2), data2, GL_STREAM_READ); |
| if (!CheckBinding(m_dispatch_buffer[1])) |
| return ERROR; |
| |
| glUseProgram(m_program); |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer[0]); |
| |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 1, 2, 3); |
| if (!RunIteration(0, 6)) |
| return ERROR; |
| |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 2, 3, 4); |
| if (!RunIteration(4, 24)) |
| return ERROR; |
| |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 4, 5, 6); |
| if (!RunIteration(12, 120)) |
| return ERROR; |
| |
| glBufferSubData(GL_DISPATCH_INDIRECT_BUFFER, 20, 12, data); |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 1, 2, 3); |
| if (!RunIteration(20, 6)) |
| return ERROR; |
| |
| GLuint* ptr = static_cast<GLuint*>( |
| glMapBufferRange(GL_DISPATCH_INDIRECT_BUFFER, 0, sizeof(GLuint) * 4, GL_MAP_WRITE_BIT)); |
| *ptr++ = 4; |
| *ptr++ = 4; |
| *ptr++ = 4; |
| glUnmapBuffer(GL_DISPATCH_INDIRECT_BUFFER); |
| |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 4, 4, 4); |
| if (!RunIteration(0, 64)) |
| return ERROR; |
| |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, m_dispatch_buffer[1]); |
| |
| glUniform3ui(glGetUniformLocation(m_program, "g_global_size"), 1, 4, 4); |
| if (!RunIteration(4, 16)) |
| return ERROR; |
| |
| glDeleteBuffers(2, m_dispatch_buffer); |
| memset(m_dispatch_buffer, 0, sizeof(m_dispatch_buffer)); |
| |
| if (!CheckBinding(0)) |
| return ERROR; |
| |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| glDeleteProgram(m_program); |
| glDeleteBuffers(1, &m_storage_buffer); |
| glDeleteBuffers(2, m_dispatch_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicSSOComputePipeline : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Separable CS Programs - Compute and non-compute stages (1)"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that compute and non-compute stages can be attached to one pipeline object." NL |
| "2. Verify that DrawArrays and ComputeDispatch commands works as expected in this case."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create VS, FS and CS. Attach all created stages to one pipeline object." NL |
| "2. Bind pipeline object." NL "3. Invoke compute stage with DispatchCompute commmand." NL |
| "4. Issue MemoryBarrier command." NL |
| "5. Issue DrawArrays command which uses data written by the compute stage." NL "6. Verify result."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_vsp, m_fsp, m_csp; |
| GLuint m_storage_buffer; |
| GLuint m_vertex_array; |
| GLuint m_pipeline; |
| |
| virtual long Setup() |
| { |
| m_vsp = m_fsp = m_csp = 0; |
| m_storage_buffer = 0; |
| m_vertex_array = 0; |
| m_pipeline = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = NL |
| "layout(local_size_x = 4) in;" NL "layout(std430) buffer Output {" NL " vec4 g_output[4];" NL "};" NL |
| "void main() {" NL " const vec2 quad[4] = vec2[](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));" NL |
| " g_output[gl_GlobalInvocationID.x] = vec4(quad[gl_GlobalInvocationID.x], 0, 1);" NL "}"; |
| m_csp = CreateComputeProgram(glsl_cs); |
| glProgramParameteri(m_csp, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| glLinkProgram(m_csp); |
| if (!CheckProgram(m_csp)) |
| return ERROR; |
| |
| const char* const glsl_vs = |
| NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; |
| m_vsp = BuildShaderProgram(GL_VERTEX_SHADER, glsl_vs); |
| if (!CheckProgram(m_vsp)) |
| return ERROR; |
| |
| const char* const glsl_fs = NL "layout(location = 0) out mediump vec4 o_color;" NL "void main() {" NL |
| " o_color = vec4(0, 1, 0, 1);" NL "}"; |
| m_fsp = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_fs); |
| if (!CheckProgram(m_fsp)) |
| return ERROR; |
| |
| glGenProgramPipelines(1, &m_pipeline); |
| glUseProgramStages(m_pipeline, GL_VERTEX_SHADER_BIT, m_vsp); |
| glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, m_fsp); |
| glUseProgramStages(m_pipeline, GL_COMPUTE_SHADER_BIT, m_csp); |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * 4, NULL, GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| glGenVertexArrays(1, &m_vertex_array); |
| glBindVertexArray(m_vertex_array); |
| glBindBuffer(GL_ARRAY_BUFFER, m_storage_buffer); |
| glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glEnableVertexAttribArray(0); |
| glBindVertexArray(0); |
| |
| glBindProgramPipeline(m_pipeline); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glDispatchCompute(1, 1, 1); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindVertexArray(m_vertex_array); |
| glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) |
| return ERROR; |
| return NO_ERROR; |
| } |
| |
| virtual long Cleanup() |
| { |
| glDeleteProgram(m_vsp); |
| glDeleteProgram(m_fsp); |
| glDeleteProgram(m_csp); |
| glDeleteBuffers(1, &m_storage_buffer); |
| glDeleteVertexArrays(1, &m_vertex_array); |
| glDeleteProgramPipelines(1, &m_pipeline); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicSSOCase2 : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Separable CS Programs - Compute and non-compute stages (2)"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that data computed by the compute stage is visible to non-compute stage after " |
| "MemoryBarrier command." NL "2. Verify that ProgramParameteri(program, GL_PROGRAM_SEPARABLE, " |
| "GL_TRUE) command works correctly for CS." NL |
| "3. Verify that gl_WorkGroupSize built-in variable is a contant and can be used as an array size."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create VS, FS and CS. Attach all created stages to one pipeline object." NL |
| "2. Bind pipeline object." NL "3. Invoke compute stage with DispatchCompute commmand." NL |
| "4. Issue MemoryBarrier command." NL |
| "5. Issue DrawArrays command which uses data written to the buffer object by the compute stage." NL |
| "6. Verify result."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program_ab; |
| GLuint m_program_c; |
| GLuint m_pipeline; |
| GLuint m_storage_buffer; |
| GLuint m_vao; |
| |
| virtual long Setup() |
| { |
| m_program_ab = 0; |
| m_program_c = 0; |
| m_pipeline = 0; |
| m_storage_buffer = 0; |
| m_vao = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| GLint res; |
| glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &res); |
| if (res <= 0) |
| { |
| OutputNotSupported("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS <= 0"); |
| return NO_ERROR; |
| } |
| |
| const char* const glsl_a = |
| "#version 310 es" NL "layout(binding = 1, std430) buffer Input {" NL " mediump vec2 g_input[4];" NL "};" NL |
| "flat out mediump vec3 color;" NL "void main() {" NL |
| " gl_Position = vec4(g_input[gl_VertexID], 0.0, 1.0);" NL " color = vec3(0.0, 1.0, 0.0);" NL "}"; |
| const char* const glsl_b = |
| "#version 310 es" NL "flat in mediump vec3 color;" NL "layout(location = 0) out mediump vec4 g_color;" NL |
| "void main() {" NL " g_color = vec4(color, 1.0);" NL "}"; |
| const char* const glsl_c = |
| "#version 310 es" NL "layout(local_size_x = 4) in;" NL "layout(binding = 1, std430) buffer Output {" NL |
| " vec2 g_output[gl_WorkGroupSize.x];" NL "};" NL "void main() {" NL |
| " if (gl_GlobalInvocationID.x == 0u) {" NL " g_output[0] = vec2(-0.8, -0.8);" NL |
| " } else if (gl_GlobalInvocationID.x == 1u) {" NL " g_output[1] = vec2(0.8, -0.8);" NL |
| " } else if (gl_GlobalInvocationID.x == 2u) {" NL " g_output[2] = vec2(-0.8, 0.8);" NL |
| " } else if (gl_GlobalInvocationID.x == 3u) {" NL " g_output[3] = vec2(0.8, 0.8);" NL " }" NL "}"; |
| |
| m_program_ab = glCreateProgram(); |
| GLuint sh = glCreateShader(GL_VERTEX_SHADER); |
| glAttachShader(m_program_ab, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_a, NULL); |
| glCompileShader(sh); |
| |
| sh = glCreateShader(GL_FRAGMENT_SHADER); |
| glAttachShader(m_program_ab, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_b, NULL); |
| glCompileShader(sh); |
| |
| glProgramParameteri(m_program_ab, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| glLinkProgram(m_program_ab); |
| |
| m_program_c = glCreateShaderProgramv(GL_COMPUTE_SHADER, 1, &glsl_c); |
| |
| glGenVertexArrays(1, &m_vao); |
| glGenProgramPipelines(1, &m_pipeline); |
| glUseProgramStages(m_pipeline, GL_ALL_SHADER_BITS, m_program_ab); |
| glUseProgramStages(m_pipeline, GL_COMPUTE_SHADER_BIT, m_program_c); |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec2) * 4, NULL, GL_STREAM_DRAW); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindProgramPipeline(m_pipeline); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); |
| glBindVertexArray(m_vao); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| if (getWindowWidth() < 500 && |
| !ValidateReadBufferCenteredQuad(getWindowWidth(), getWindowHeight(), vec3(0, 1, 0))) |
| { |
| return ERROR; |
| } |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glDeleteProgram(m_program_ab); |
| glDeleteProgram(m_program_c); |
| glDeleteProgramPipelines(1, &m_pipeline); |
| glDeleteBuffers(1, &m_storage_buffer); |
| glDeleteVertexArrays(1, &m_vao); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicSSOCase3 : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Separable CS Programs - Compute stage"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that compute shader stage selected with UseProgram command has precedence" NL |
| "over compute shader stage selected with BindProgramPipeline command."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create CS0 with CreateProgram command. Create CS1 with CreateShaderProgramv command." NL |
| "2. Verify that CS program selected with UseProgram is dispatched even if there is active" NL |
| " compute stage bound by BindProgramPipeline."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program_a; |
| GLuint m_program_b; |
| GLuint m_pipeline; |
| GLuint m_storage_buffer; |
| |
| virtual long Setup() |
| { |
| m_program_a = 0; |
| m_program_b = 0; |
| m_pipeline = 0; |
| m_storage_buffer = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_a = |
| "#version 310 es" NL "layout(local_size_x = 1) in;" NL "layout(binding = 3, std430) buffer Output {" NL |
| " int g_output;" NL "};" NL "void main() {" NL " g_output = 1;" NL "}"; |
| const char* const glsl_b = |
| "#version 310 es" NL "layout(local_size_x = 1) in;" NL "layout(binding = 3, std430) buffer Output {" NL |
| " int g_output;" NL "};" NL "void main() {" NL " g_output = 2;" NL "}"; |
| /* create program A */ |
| { |
| m_program_a = glCreateProgram(); |
| GLuint sh = glCreateShader(GL_COMPUTE_SHADER); |
| glAttachShader(m_program_a, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_a, NULL); |
| glCompileShader(sh); |
| glProgramParameteri(m_program_a, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| glLinkProgram(m_program_a); |
| } |
| m_program_b = glCreateShaderProgramv(GL_COMPUTE_SHADER, 1, &glsl_b); |
| |
| /* create storage buffer */ |
| { |
| int data = 0; |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(int), &data, GL_STREAM_READ); |
| } |
| |
| glGenProgramPipelines(1, &m_pipeline); |
| glUseProgramStages(m_pipeline, GL_ALL_SHADER_BITS, m_program_b); |
| |
| glUseProgram(m_program_a); |
| glBindProgramPipeline(m_pipeline); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| long error = NO_ERROR; |
| { |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), GL_MAP_READ_BIT)); |
| if (data[0] != 1) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is " << data[0] << " should be 1." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| glUseProgram(0); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| { |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), GL_MAP_READ_BIT)); |
| if (data[0] != 2) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is " << data[0] << " should be 2." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| glUseProgram(m_program_b); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| { |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), GL_MAP_READ_BIT)); |
| if (data[0] != 2) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is " << data[0] << " should be 2." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| glUseProgram(0); |
| glUseProgramStages(m_pipeline, GL_COMPUTE_SHADER_BIT, m_program_a); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| { |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), GL_MAP_READ_BIT)); |
| if (data[0] != 1) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is " << data[0] << " should be 1." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return error; |
| } |
| virtual long Cleanup() |
| { |
| glDeleteProgram(m_program_a); |
| glDeleteProgram(m_program_b); |
| glDeleteProgramPipelines(1, &m_pipeline); |
| glDeleteBuffers(1, &m_storage_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicAtomicCase1 : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Atomic functions"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that atomicAdd function works as expected with int and uint parameters." NL |
| "2. Verify that shared memory can be used with atomic functions." NL |
| "3. Verify that groupMemoryBarrier() and barrier() built-in functions work as expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Use shared memory as a 'counter' with-in one CS work group." NL |
| "2. Each shader invocation increments/decrements 'counter' value using atomicAdd function." NL |
| "3. Values returned by atomicAdd function are written to SSBO." NL |
| "4. Verify SSBO content (values from 0 to 7 should be written)."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "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 = 8) in;" NL "layout(std430, binding = 0) buffer Output {" NL |
| " uint g_add_output[8];" NL " int g_sub_output[8];" NL "};" NL "shared uint g_add_value;" NL |
| "shared int g_sub_value;" NL "void main() {" NL " if (gl_LocalInvocationIndex == 0u) {" NL |
| " g_add_value = 0u;" NL " g_sub_value = 7;" NL " }" NL |
| " g_add_output[gl_LocalInvocationIndex] = 0u;" NL " g_sub_output[gl_LocalInvocationIndex] = 0;" NL |
| " groupMemoryBarrier();" NL " barrier();" NL |
| " g_add_output[gl_LocalInvocationIndex] = atomicAdd(g_add_value, 1u);" NL |
| " g_sub_output[gl_LocalInvocationIndex] = atomicAdd(g_sub_value, -1);" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 16 * sizeof(int), NULL, GL_STATIC_DRAW); |
| |
| glUseProgram(m_program); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| int* data; |
| data = static_cast<int*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int) * 8, GL_MAP_READ_BIT)); |
| std::sort(data, data + 8); |
| long error = NO_ERROR; |
| for (int i = 0; i < 8; ++i) |
| { |
| if (data[i] != i) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << data[i] << " should be " << i << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| |
| data = static_cast<int*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, sizeof(int) * 8, sizeof(int) * 8, GL_MAP_READ_BIT)); |
| std::sort(data, data + 8); |
| for (int i = 0; i < 8; ++i) |
| { |
| if (data[i] != i) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << data[i] << " should be " << i << tcu::TestLog::EndMessage; |
| 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_storage_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicAtomicCase2 : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Atomic functions - buffer variables"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that all atomic functions (atomicExchange, atomicMin, atomicMax," NL |
| " atomicAnd, atomicOr, atomicXor and atomicCompSwap) works as expected with buffer variables." NL |
| "2. Verify that atomic functions work with parameters being constants and" NL |
| " with parameters being uniforms." NL |
| "3. Verify that barrier() built-in function can be used in a control flow."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create CS that uses all atomic functions. Values returned by the atomic functions are written to " |
| "SSBO." NL "2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL |
| "3. Verify SSBO content." NL |
| "4. Repeat for different number of work groups and different work group sizes."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_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 "const uvec3 kGlobalSize = uvec3(" << global_size.x() |
| << ", " << global_size.y() << ", " << global_size.z() |
| << ");" NL "layout(std430, binding = 0) buffer OutputU {" NL " uint g_uint_out[" |
| << global_size.x() * global_size.y() * global_size.z() |
| << "];" NL "};" NL "layout(std430, binding = 1) buffer OutputI {" NL " int data[" |
| << global_size.x() * global_size.y() * global_size.z() |
| << "];" NL "} g_int_out;" NL "uniform uint g_uint_value[8];" NL "void main() {" NL |
| " uint global_index = gl_GlobalInvocationID.x +" NL |
| " gl_GlobalInvocationID.y * kGlobalSize.x +" NL |
| " gl_GlobalInvocationID.z * kGlobalSize.x * kGlobalSize.y;" NL |
| " atomicExchange(g_uint_out[global_index], g_uint_value[0]);" NL |
| " atomicMin(g_uint_out[global_index], g_uint_value[1]);" NL |
| " atomicMax(g_uint_out[global_index], g_uint_value[2]);" NL |
| " atomicAnd(g_uint_out[global_index], g_uint_value[3]);" NL |
| " atomicOr(g_uint_out[global_index], g_uint_value[4]);" NL " if (g_uint_value[0] > 0u) {" NL |
| " barrier();" // not needed here, just check if compiler accepts it in a control flow |
| NL " atomicXor(g_uint_out[global_index], g_uint_value[5]);" NL " }" NL |
| " atomicCompSwap(g_uint_out[global_index], g_uint_value[6], g_uint_value[7]);" NL NL |
| " atomicExchange(g_int_out.data[global_index], 3);" NL " atomicMin(g_int_out.data[global_index], 1);" NL |
| " atomicMax(g_int_out.data[global_index], 2);" NL " atomicAnd(g_int_out.data[global_index], 0x1);" NL |
| " atomicOr(g_int_out.data[global_index], 0x3);" NL " atomicXor(g_int_out.data[global_index], 0x1);" NL |
| " atomicCompSwap(g_int_out.data[global_index], 0x2, 0x7);" 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(); |
| |
| if (m_storage_buffer[0] == 0) |
| glGenBuffers(2, m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer[0]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint) * kBufferSize, NULL, GL_DYNAMIC_DRAW); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storage_buffer[1]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLint) * kBufferSize, NULL, GL_DYNAMIC_DRAW); |
| |
| glUseProgram(m_program); |
| GLuint values[8] = { 3u, 1u, 2u, 0x1u, 0x3u, 0x1u, 0x2u, 0x7u }; |
| glUniform1uiv(glGetUniformLocation(m_program, "g_uint_value"), 8, values); |
| 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_BUFFER_UPDATE_BARRIER_BIT); |
| |
| bool res = true; |
| GLuint* udata; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[0]); |
| udata = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * kBufferSize, GL_MAP_READ_BIT)); |
| for (GLuint i = 0; i < kBufferSize; ++i) |
| { |
| if (udata[i] != 7) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "uData at index " << i << " is " |
| << udata[i] << " should be 7." << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| GLint* idata; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[1]); |
| idata = static_cast<GLint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLint) * kBufferSize, GL_MAP_READ_BIT)); |
| for (GLint i = 0; i < static_cast<GLint>(kBufferSize); ++i) |
| { |
| if (idata[i] != 7) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "iData at index " << i << " is " |
| << idata[i] << " should be 7." << tcu::TestLog::EndMessage; |
| res = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return res; |
| } |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer[0] = m_storage_buffer[1] = 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(2, m_storage_buffer); |
| glDeleteBuffers(1, &m_dispatch_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| class BasicAtomicCase3 : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Atomic functions - shared variables"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that all atomic functions (atomicExchange, atomicMin, atomicMax," NL |
| " atomicAnd, atomicOr, atomicXor and atomicCompSwap) works as expected with shared variables." NL |
| "2. Verify that atomic functions work with parameters being constants and" NL |
| " with parameters being uniforms." NL |
| "3. Verify that atomic functions can be used in a control flow."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create CS that uses all atomic functions. Values returned by the atomic functions are written to " |
| "SSBO." NL "2. Dispatch CS with DispatchCompute and DispatchComputeIndirect commands." NL |
| "3. Verify SSBO content." NL |
| "4. Repeat for different number of work groups and different work group sizes."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer; |
| GLuint m_dispatch_buffer; |
| |
| std::string GenSource(const uvec3& local_size) |
| { |
| 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 g_uint_out[" |
| << local_size.x() * local_size.y() * local_size.z() << "];" NL " int g_int_out[" |
| << local_size.x() * local_size.y() * local_size.z() << "];" NL "};" NL "shared uint g_shared_uint[" |
| << local_size.x() * local_size.y() * local_size.z() << "];" NL "shared int g_shared_int[" |
| << local_size.x() * local_size.y() * local_size.z() |
| << "];" NL "uniform uint g_uint_value[8];" NL "void main() {" NL |
| " atomicExchange(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[0]);" NL |
| " atomicMin(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[1]);" NL |
| " atomicMax(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[2]);" NL |
| " atomicAnd(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[3]);" NL |
| " atomicOr(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[4]);" NL |
| " atomicXor(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[5]);" NL |
| " atomicCompSwap(g_shared_uint[gl_LocalInvocationIndex], g_uint_value[6], g_uint_value[7]);" NL NL |
| " atomicExchange(g_shared_int[gl_LocalInvocationIndex], 3);" NL |
| " atomicMin(g_shared_int[gl_LocalInvocationIndex], 1);" NL |
| " atomicMax(g_shared_int[gl_LocalInvocationIndex], 2);" NL |
| " atomicAnd(g_shared_int[gl_LocalInvocationIndex], 0x1);" NL " if (g_uint_value[1] > 0u) {" NL |
| " atomicOr(g_shared_int[gl_LocalInvocationIndex], 0x3);" NL |
| " atomicXor(g_shared_int[gl_LocalInvocationIndex], 0x1);" NL |
| " atomicCompSwap(g_shared_int[gl_LocalInvocationIndex], 0x2, 0x7);" NL " }" NL NL |
| " g_uint_out[gl_LocalInvocationIndex] = g_shared_uint[gl_LocalInvocationIndex];" NL |
| " g_int_out[gl_LocalInvocationIndex] = g_shared_int[gl_LocalInvocationIndex];" NL "}"; |
| return ss.str(); |
| } |
| bool RunIteration(const uvec3& local_size, bool dispatch_indirect) |
| { |
| if (m_program != 0) |
| glDeleteProgram(m_program); |
| m_program = CreateComputeProgram(GenSource(local_size)); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return false; |
| |
| const GLuint kBufferSize = local_size.x() * local_size.y() * local_size.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(GLuint) * kBufferSize * 2, NULL, GL_DYNAMIC_DRAW); |
| |
| glUseProgram(m_program); |
| GLuint values[8] = { 3u, 1u, 2u, 0x1u, 0x3u, 0x1u, 0x2u, 0x7u }; |
| glUniform1uiv(glGetUniformLocation(m_program, "g_uint_value"), 8, values); |
| 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[0], GL_STATIC_DRAW); |
| glDispatchComputeIndirect(0); |
| } |
| else |
| { |
| glDispatchCompute(1, 1, 1); |
| } |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| bool ret = true; |
| GLuint* udata; |
| udata = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * kBufferSize, GL_MAP_READ_BIT)); |
| for (GLuint i = 0; i < kBufferSize; ++i) |
| { |
| if (udata[i] != 7) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "uData at index " << i << " is " |
| << udata[i] << " should be 7." << tcu::TestLog::EndMessage; |
| ret = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| |
| GLint* idata; |
| idata = static_cast<GLint*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint) * kBufferSize, |
| sizeof(GLint) * kBufferSize, GL_MAP_READ_BIT)); |
| for (GLint i = 0; i < static_cast<GLint>(kBufferSize); ++i) |
| { |
| if (idata[i] != 7) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "iData at index " << i << " is " |
| << idata[i] << " should be 7." << 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(uvec3(64, 1, 1), false)) |
| return ERROR; |
| if (!RunIteration(uvec3(1, 1, 64), true)) |
| return ERROR; |
| if (!RunIteration(uvec3(1, 1, 4), false)) |
| return ERROR; |
| if (!RunIteration(uvec3(3, 2, 1), true)) |
| return ERROR; |
| if (!RunIteration(uvec3(2, 4, 2), false)) |
| return ERROR; |
| if (!RunIteration(uvec3(2, 4, 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 AdvancedCopyImage : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Copy Image"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that copying two textures using CS works as expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL "Use shader image load and store operations to copy two textures in the CS."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_texture[2]; |
| GLuint m_fbo; |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_fbo = 0; |
| memset(m_texture, 0, sizeof(m_texture)); |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "#define TILE_WIDTH 8" NL "#define TILE_HEIGHT 8" NL |
| "const ivec2 kTileSize = ivec2(TILE_WIDTH, TILE_HEIGHT);" NL NL |
| "layout(binding = 0, rgba8) readonly uniform mediump image2D g_input_image;" NL |
| "layout(binding = 1, rgba8) writeonly uniform mediump image2D g_output_image;" NL NL |
| "layout(local_size_x=TILE_WIDTH, local_size_y=TILE_HEIGHT) in;" NL NL "void main() {" NL |
| " ivec2 tile_xy = ivec2(gl_WorkGroupID);" NL " ivec2 thread_xy = ivec2(gl_LocalInvocationID);" NL |
| " ivec2 pixel_xy = tile_xy * kTileSize + thread_xy;" NL NL |
| " vec4 pixel = imageLoad(g_input_image, pixel_xy);" NL |
| " imageStore(g_output_image, pixel_xy, pixel);" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| std::vector<GLubyte> in_image(64 * 64 * 4, 0x0f); |
| std::vector<GLubyte> out_image(64 * 64 * 4, 0xff); |
| |
| glGenTextures(2, m_texture); |
| glBindTexture(GL_TEXTURE_2D, m_texture[0]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 64, 64); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, &in_image[0]); |
| |
| glBindTexture(GL_TEXTURE_2D, m_texture[1]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 64, 64); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, &out_image[0]); |
| |
| glUseProgram(m_program); |
| glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); |
| glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); |
| glDispatchCompute(9, 8, |
| 1); // 9 is on purpose, to ensure that out of bounds image load and stores have no effect |
| glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); |
| |
| std::vector<GLubyte> data(64 * 64 * 4); |
| glGenFramebuffers(1, &m_fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture[1], 0); |
| glReadPixels(0, 0, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| for (std::size_t i = 0; i < data.size(); ++i) |
| { |
| if (data[i] != 0x0f) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << data[i] << " should be " << 0x0f << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| glDeleteProgram(m_program); |
| glDeleteFramebuffers(1, &m_fbo); |
| glDeleteTextures(2, m_texture); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedPipelinePreVS : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "CS as an additional pipeline stage - Before VS (1)"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that CS which runs just before VS and modifies VBO content works as expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Prepare VBO and VAO for a drawing operation." NL "2. Run CS to modify existing VBO content." NL |
| "3. Issue MemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT) command." NL |
| "4. Issue draw call command." NL "5. Verify that the framebuffer content is as expected."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program[2]; |
| GLuint m_vertex_buffer; |
| GLuint m_vertex_array; |
| |
| virtual long Setup() |
| { |
| memset(m_program, 0, sizeof(m_program)); |
| m_vertex_buffer = 0; |
| m_vertex_array = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = NL "layout(local_size_x = 4) in;" NL "struct Vertex {" NL " vec4 position;" NL |
| " vec4 color;" NL "};" NL "layout(binding = 0, std430) buffer VertexBuffer {" NL |
| " Vertex g_vertex[];" NL "};" NL "uniform float g_scale;" NL "void main() {" NL |
| " g_vertex[gl_GlobalInvocationID.x].position.xyz *= g_scale;" NL |
| " g_vertex[gl_GlobalInvocationID.x].color *= vec4(0.0, 1.0, 0.0, 1.0);" NL "}"; |
| m_program[0] = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program[0]); |
| glUseProgram(m_program[0]); |
| glUniform1f(glGetUniformLocation(m_program[0], "g_scale"), 0.8f); |
| glUseProgram(0); |
| if (!CheckProgram(m_program[0])) |
| return ERROR; |
| |
| const char* const glsl_vs = |
| NL "layout(location = 0) in mediump vec4 g_position;" NL "layout(location = 1) in mediump vec4 g_color;" NL |
| "flat out mediump vec4 color;" NL "void main() {" NL " gl_Position = g_position;" NL |
| " color = g_color;" NL "}"; |
| const char* const glsl_fs = |
| NL "flat in mediump vec4 color;" NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL |
| " g_color = color;" NL "}"; |
| m_program[1] = CreateProgram(glsl_vs, glsl_fs); |
| glLinkProgram(m_program[1]); |
| if (!CheckProgram(m_program[1])) |
| return ERROR; |
| |
| /* vertex buffer */ |
| { |
| const float data[] = { -1, -1, 0, 1, 1, 1, 1, 1, 1, -1, 0, 1, 1, 1, 1, 1, |
| -1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 }; |
| glGenBuffers(1, &m_vertex_buffer); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| } |
| |
| glGenVertexArrays(1, &m_vertex_array); |
| glBindVertexArray(m_vertex_array); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 2 * sizeof(vec4), 0); |
| glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 2 * sizeof(vec4), reinterpret_cast<void*>(sizeof(vec4))); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glEnableVertexAttribArray(0); |
| glEnableVertexAttribArray(1); |
| glBindVertexArray(0); |
| |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_vertex_buffer); |
| glUseProgram(m_program[0]); |
| glDispatchCompute(1, 1, 1); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glUseProgram(m_program[1]); |
| glBindVertexArray(m_vertex_array); |
| glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT); |
| glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 1); |
| |
| if (getWindowWidth() < 500 && |
| !ValidateReadBufferCenteredQuad(getWindowWidth(), getWindowHeight(), vec3(0, 1, 0))) |
| { |
| return ERROR; |
| } |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| for (int i = 0; i < 2; ++i) |
| glDeleteProgram(m_program[i]); |
| glDeleteBuffers(1, &m_vertex_buffer); |
| glDeleteVertexArrays(1, &m_vertex_array); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedPipelineGenDrawCommands : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "CS as an additional pipeline stage - Before VS (2)"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that a complex scenario where CS is used to generate drawing commands" NL |
| "and write them to a draw indirect buffer works as expected. This is a practial usage of CS." NL |
| "CS is used for culling objects which are outside of the viewing frustum."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Run CS which will generate four sets of draw call parameters and write them to the draw indirect " |
| "buffer." NL "2. One set of draw call parameters will be: 0, 0, 0, 0" NL |
| " (which means that an object is outside of the viewing frustum and should not be drawn)." NL |
| "3. Issue MemoryBarrier(GL_COMMAND_BARRIER_BIT) command." NL |
| "4. Issue four draw indirect commands." NL "5. Verify that the framebuffer content is as expected."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program[2]; |
| GLuint m_vertex_buffer; |
| GLuint m_index_buffer; |
| GLuint m_vertex_array; |
| GLuint m_draw_buffer; |
| GLuint m_object_buffer; |
| |
| virtual long Setup() |
| { |
| memset(m_program, 0, sizeof(m_program)); |
| m_vertex_buffer = 0; |
| m_index_buffer = 0; |
| m_vertex_array = 0; |
| m_draw_buffer = 0; |
| m_object_buffer = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| GLint res; |
| glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &res); |
| if (res <= 0) |
| { |
| OutputNotSupported("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS <= 0"); |
| return NOT_SUPPORTED; |
| } |
| |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 4) in;" NL "struct DrawCommand {" NL " uint count;" NL |
| " uint instance_count;" NL " uint first_index;" NL " int base_vertex;" NL " uint base_instance;" NL |
| "};" NL "layout(std430) buffer;" NL "layout(binding = 0) readonly buffer ObjectBuffer {" NL |
| " mat4 transform[4];" NL " uint count[4];" NL " uint first_index[4];" NL "} g_objects;" NL |
| "layout(binding = 1) writeonly buffer DrawCommandBuffer {" NL " DrawCommand g_command[4];" NL "};" NL |
| "bool IsObjectVisible(uint id) {" NL |
| " if (g_objects.transform[id][3].x < -1.0 || g_objects.transform[id][3].x > 1.0) return false;" NL |
| " if (g_objects.transform[id][3][1] < -1.0 || g_objects.transform[id][3][1] > 1.0) return false;" NL |
| " if (g_objects.transform[id][3][2] < -1.0 || g_objects.transform[id][3].z > 1.0) return false;" NL |
| " return true;" NL "}" NL "void main() {" NL " uint id = gl_GlobalInvocationID.x;" NL |
| " g_command[id].count = 0u;" NL " g_command[id].instance_count = 0u;" NL |
| " g_command[id].first_index = 0u;" NL " g_command[id].base_vertex = int(0);" NL |
| " g_command[id].base_instance = 0u;" NL " if (IsObjectVisible(id)) {" NL |
| " g_command[id].count = g_objects.count[id];" NL " g_command[id].instance_count = 1u;" NL |
| " g_command[id].first_index = g_objects.first_index[id];" NL " }" NL "}"; |
| m_program[0] = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program[0]); |
| if (!CheckProgram(m_program[0])) |
| return ERROR; |
| |
| const char* const glsl_vs = |
| NL "layout(location = 0) in mediump vec4 g_position;" NL "layout(location = 1) in mediump vec3 g_color;" NL |
| "flat out mediump vec3 color;" NL "layout(binding = 0, std430) buffer ObjectBuffer {" NL |
| " mediump mat4 transform[4];" NL " uint count[4];" NL " uint first_index[4];" NL "} g_objects;" NL |
| "uniform int g_object_id;" NL "void main() {" NL |
| " gl_Position = g_objects.transform[g_object_id] * g_position;" NL " color = g_color;" NL "}"; |
| const char* const glsl_fs = |
| NL "flat in mediump vec3 color;" NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL |
| " g_color = vec4(color, 1.0);" NL "}"; |
| m_program[1] = CreateProgram(glsl_vs, glsl_fs); |
| glLinkProgram(m_program[1]); |
| if (!CheckProgram(m_program[1])) |
| return ERROR; |
| glViewport(0, 0, 100, 100); |
| |
| /* object buffer */ |
| { |
| struct |
| { |
| mat4 transform[4]; |
| GLuint count[4]; |
| GLuint first_index[4]; |
| } data = { |
| { tcu::translationMatrix(vec3(-1.5f, -0.5f, 0.0f)), tcu::translationMatrix(vec3(0.5f, -0.5f, 0.0f)), |
| tcu::translationMatrix(vec3(-0.5f, 0.5f, 0.0f)), tcu::translationMatrix(vec3(0.5f, 0.5f, 0.0f)) }, |
| { 4, 4, 4, 4 }, |
| { 0, 4, 8, 12 } |
| }; |
| glGenBuffers(1, &m_object_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_object_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), &data, GL_STATIC_DRAW); |
| } |
| /* vertex buffer */ |
| { |
| const vec3 data[] = { vec3(-0.4f, -0.4f, 0.0f), vec3(1, 0, 0), vec3(0.4f, -0.4f, 0.0f), vec3(1, 0, 0), |
| vec3(-0.4f, 0.4f, 0.0f), vec3(1, 0, 0), vec3(0.4f, 0.4f, 0.0f), vec3(1, 0, 0), |
| vec3(-0.4f, -0.4f, 0.0f), vec3(0, 1, 0), vec3(0.4f, -0.4f, 0.0f), vec3(0, 1, 0), |
| vec3(-0.4f, 0.4f, 0.0f), vec3(0, 1, 0), vec3(0.4f, 0.4f, 0.0f), vec3(0, 1, 0), |
| vec3(-0.4f, -0.4f, 0.0f), vec3(0, 0, 1), vec3(0.4f, -0.4f, 0.0f), vec3(0, 0, 1), |
| vec3(-0.4f, 0.4f, 0.0f), vec3(0, 0, 1), vec3(0.4f, 0.4f, 0.0f), vec3(0, 0, 1), |
| vec3(-0.4f, -0.4f, 0.0f), vec3(1, 1, 0), vec3(0.4f, -0.4f, 0.0f), vec3(1, 1, 0), |
| vec3(-0.4f, 0.4f, 0.0f), vec3(1, 1, 0), vec3(0.4f, 0.4f, 0.0f), vec3(1, 1, 0) }; |
| glGenBuffers(1, &m_vertex_buffer); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| } |
| /* index buffer */ |
| { |
| const GLushort data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; |
| glGenBuffers(1, &m_index_buffer); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(data), data, GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| glGenBuffers(1, &m_draw_buffer); |
| glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_draw_buffer); |
| glBufferData(GL_DRAW_INDIRECT_BUFFER, 4 * sizeof(GLuint) * 5, NULL, GL_DYNAMIC_DRAW); |
| glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); |
| |
| glGenVertexArrays(1, &m_vertex_array); |
| glBindVertexArray(m_vertex_array); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(vec3), 0); |
| glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(vec3), reinterpret_cast<void*>(sizeof(vec3))); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glEnableVertexAttribArray(0); |
| glEnableVertexAttribArray(1); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); |
| glBindVertexArray(0); |
| |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_draw_buffer); |
| glUseProgram(m_program[0]); |
| glDispatchCompute(1, 1, 1); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glUseProgram(m_program[1]); |
| glBindVertexArray(m_vertex_array); |
| glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_draw_buffer); |
| glMemoryBarrier(GL_COMMAND_BARRIER_BIT); |
| /* draw (CPU draw calls dispatch, could be done by the GPU with ARB_multi_draw_indirect) */ |
| { |
| GLsizeiptr offset = 0; |
| for (int i = 0; i < 4; ++i) |
| { |
| glUniform1i(glGetUniformLocation(m_program[1], "g_object_id"), i); |
| glDrawElementsIndirect(GL_TRIANGLE_STRIP, GL_UNSIGNED_SHORT, reinterpret_cast<void*>(offset)); |
| offset += 5 * sizeof(GLuint); |
| } |
| } |
| if (getWindowWidth() >= 100 && getWindowHeight() >= 100 && |
| !ValidateWindow4Quads(vec3(0), vec3(0, 1, 0), vec3(1, 1, 0), vec3(0, 0, 1))) |
| { |
| return ERROR; |
| } |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| for (int i = 0; i < 2; ++i) |
| glDeleteProgram(m_program[i]); |
| glDeleteBuffers(1, &m_vertex_buffer); |
| glDeleteBuffers(1, &m_index_buffer); |
| glDeleteVertexArrays(1, &m_vertex_array); |
| glDeleteBuffers(1, &m_draw_buffer); |
| glDeleteBuffers(1, &m_object_buffer); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedPipelineComputeChain : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Compute Chain"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that dispatching several compute kernels that work in a sequence" NL |
| " with a common set of resources works as expected." NL |
| "2. Verify that indexing nested structures with built-in variables work as expected." NL |
| "3. Verify that two kernels can write to the same resource without MemoryBarrier" NL |
| " command if target regions of memory do not overlap."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create a set of GPU resources (buffers, images, atomic counters)." NL |
| "2. Dispatch Kernel0 that write to these resources." NL "3. Issue MemoryBarrier command." NL |
| "4. Dispatch Kernel1 that read/write from/to these resources." NL "5. Issue MemoryBarrier command." NL |
| "6. Dispatch Kernel2 that read/write from/to these resources." NL |
| "7. Verify that content of all resources is as expected."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program[3]; |
| GLuint m_storage_buffer[4]; |
| GLuint m_counter_buffer; |
| GLuint m_texture; |
| GLuint m_fbo; |
| |
| std::string Common() |
| { |
| return NL "precision highp image2D;" NL "struct S0 {" NL " int m0[8];" NL "};" NL "struct S1 {" NL |
| " S0 m0[8];" NL "};" NL "layout(binding = 0, std430) buffer Buffer0 {" NL " int m0[5];" NL |
| " S1 m1[8];" NL "} g_buffer0;" NL "layout(binding = 1, std430) buffer Buffer1 {" NL |
| " uint data[8];" NL "} g_buffer1;" NL "layout(binding = 2, std430) buffer Buffer2 {" NL |
| " int data[256];" NL "} g_buffer2;" NL "layout(binding = 3, std430) buffer Buffer3 {" NL |
| " int data[256];" NL "} g_buffer3;" NL "layout(binding = 4, std430) buffer Buffer4 {" NL |
| " mat4 data0;" NL " mat4 data1;" NL "} g_buffer4;" NL |
| "layout(binding = 0, rgba8) writeonly uniform mediump image2D g_image0;" NL |
| "layout(binding = 0, offset = 8) uniform atomic_uint g_counter[2];"; |
| } |
| std::string GenGLSL(int p) |
| { |
| std::stringstream ss; |
| ss << Common(); |
| if (p == 0) |
| { |
| ss << NL "layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;" NL |
| "void UpdateBuffer0(uvec3 id, int add_val) {" NL " if (id.x < 8u && id.y < 8u && id.z < 8u) {" NL |
| " g_buffer0.m1[id.z].m0[id.y].m0[id.x] += add_val;" NL " }" NL "}" NL |
| "uniform int g_add_value;" NL "uniform uint g_counter_y;" NL "uniform vec4 g_image_value;" NL |
| "void main() {" NL " uvec3 id = gl_GlobalInvocationID;" NL " UpdateBuffer0(id, 1);" NL |
| " UpdateBuffer0(id, g_add_value);" NL " if (id == uvec3(1, g_counter_y, 1)) {" NL |
| " uint idx = atomicCounterIncrement(g_counter[1]);" NL " g_buffer1.data[idx] = idx;" NL |
| " idx = atomicCounterIncrement(g_counter[1]);" NL " g_buffer1.data[idx] = idx;" NL " }" NL |
| " if (id.x < 4u && id.y < 4u && id.z == 0u) {" NL |
| " imageStore(g_image0, ivec2(id.xy), g_image_value);" NL " }" NL |
| " if (id.x < 2u && id.y == 0u && id.z == 0u) {" NL |
| " g_buffer2.data[id.x] -= int(g_counter_y);" NL " }" NL "}"; |
| } |
| else if (p == 1) |
| { |
| ss << NL "layout(local_size_x = 4, local_size_y = 4, local_size_z = 1) in;" |
| // translation matrix |
| NL "uniform mat4 g_mvp;" NL "void main() {" NL " if (gl_GlobalInvocationID == uvec3(0)) {" NL |
| " g_buffer4.data0 *= g_mvp;" NL " }" NL " if (gl_WorkGroupID == uvec3(0)) {" NL |
| " g_buffer4.data1[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = " |
| "g_mvp[gl_LocalInvocationID.x][gl_LocalInvocationID.y];" NL " }" NL "}"; |
| } |
| else if (p == 2) |
| { |
| ss << NL "layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;" NL "void main() {" NL "}"; |
| } |
| return ss.str(); |
| } |
| virtual long Setup() |
| { |
| memset(m_program, 0, sizeof(m_program)); |
| memset(m_storage_buffer, 0, sizeof(m_storage_buffer)); |
| m_counter_buffer = 0; |
| m_texture = 0; |
| m_fbo = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| using namespace tcu; |
| |
| for (int i = 0; i < 3; ++i) |
| { |
| m_program[i] = CreateComputeProgram(GenGLSL(i)); |
| glLinkProgram(m_program[i]); |
| if (i == 0) |
| { |
| glUseProgram(m_program[i]); |
| glUniform1i(glGetUniformLocation(m_program[i], "g_add_value"), 1); |
| glUniform1ui(glGetUniformLocation(m_program[i], "g_counter_y"), 1u); |
| glUniform4f(glGetUniformLocation(m_program[i], "g_image_value"), 0.25f, 0.5f, 0.75f, 1.0f); |
| glUseProgram(0); |
| } |
| else if (i == 1) |
| { |
| glUseProgram(m_program[i]); |
| GLfloat values[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, |
| 0.0f, 0.0f, 1.0f, 0.0f, 10.0f, 20.0f, 30.0f, 1.0f }; |
| glUniformMatrix4fv(glGetUniformLocation(m_program[i], "g_mvp"), 1, GL_FALSE, values); |
| glUseProgram(0); |
| } |
| if (!CheckProgram(m_program[i])) |
| return ERROR; |
| } |
| |
| glGenBuffers(4, m_storage_buffer); |
| /* storage buffer 0 */ |
| { |
| std::vector<int> data(5 + 8 * 8 * 8); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer[0]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)(data.size() * sizeof(int)), &data[0], GL_STATIC_COPY); |
| } |
| /* storage buffer 1 */ |
| { |
| const GLuint data[8] = { 0 }; |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storage_buffer[1]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, GL_STATIC_COPY); |
| } |
| /* storage buffer 2 & 3 */ |
| { |
| std::vector<GLint> data(512, 7); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[2]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)(data.size() * sizeof(GLint)), &data[0], GL_STATIC_COPY); |
| |
| glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, m_storage_buffer[2], 0, |
| (GLsizeiptr)(sizeof(GLint) * data.size() / 2)); |
| glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, m_storage_buffer[2], |
| (GLintptr)(sizeof(GLint) * data.size() / 2), |
| (GLsizeiptr)(sizeof(GLint) * data.size() / 2)); |
| } |
| /* storage buffer 4 */ |
| { |
| std::vector<mat4> data(2); |
| data[0] = mat4(1); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_storage_buffer[3]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)(data.size() * sizeof(mat4)), &data[0], GL_STATIC_COPY); |
| } |
| /* counter buffer */ |
| { |
| GLuint data[4] = { 0 }; |
| glGenBuffers(1, &m_counter_buffer); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_counter_buffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(data), data, GL_STATIC_COPY); |
| } |
| /* texture */ |
| { |
| std::vector<GLint> data(4 * 4 * 4, 0); |
| glGenTextures(1, &m_texture); |
| glBindTexture(GL_TEXTURE_2D, m_texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| } |
| |
| glUseProgram(m_program[0]); |
| glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); |
| glDispatchCompute(2, 2, 2); |
| glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); |
| glDispatchCompute(3, 2, 2); |
| |
| glUseProgram(m_program[1]); |
| glDispatchCompute(4, 3, 7); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | |
| GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); |
| |
| long error = NO_ERROR; |
| /* validate storage buffer 0 */ |
| { |
| int* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[0]); |
| data = static_cast<int*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int) * (5 + 8 * 8 * 8), GL_MAP_READ_BIT)); |
| for (std::size_t i = 5; i < 5 + 8 * 8 * 8; ++i) |
| { |
| if (data[i] != 4) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is: " << data[i] |
| << " should be: 2." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| } |
| /* validate storage buffer 1 */ |
| { |
| GLuint* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[1]); |
| data = static_cast<GLuint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * 8, GL_MAP_READ_BIT)); |
| for (GLuint i = 0; i < 4; ++i) |
| { |
| if (data[i] != i) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is: " << data[i] |
| << " should be: " << i << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| } |
| /* validate storage buffer 2 & 3 */ |
| { |
| GLint* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[2]); |
| data = static_cast<GLint*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLint) * 512, GL_MAP_READ_BIT)); |
| for (int i = 0; i < 2; ++i) |
| { |
| if (data[i] != 5) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is: " << data[i] |
| << " should be: 5." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| if (data[i + 256] != 7) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is: " << data[i + 256] |
| << " should be: 7." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| } |
| /* validate storage buffer 4 */ |
| { |
| mat4* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[3]); |
| data = static_cast<mat4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(mat4) * 2, GL_MAP_READ_BIT)); |
| if (transpose(data[1]) != translationMatrix(vec3(10.0f, 20.0f, 30.0f))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is incorrect." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| if (transpose(data[0]) != transpose(translationMatrix(vec3(10.0f, 20.0f, 30.0f)))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data is incorrect." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| } |
| /* validate counter buffer */ |
| { |
| GLuint* data; |
| data = static_cast<GLuint*>( |
| glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint) * 4, GL_MAP_READ_BIT)); |
| if (data[3] != 4) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is: " << data[3] |
| << " should be: " << 4 << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER); |
| } |
| /* validate texture */ |
| { |
| std::vector<vec4> data(4 * 4); |
| glBindTexture(GL_TEXTURE_2D, m_texture); |
| glGenFramebuffers(1, &m_fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0); |
| std::vector<GLubyte> colorData(4 * 4 * 4); |
| glReadPixels(0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, &colorData[0]); |
| for (int i = 0; i < 4 * 4 * 4; i += 4) |
| { |
| data[i / 4] = |
| vec4(static_cast<GLfloat>(colorData[i] / 255.), static_cast<GLfloat>(colorData[i + 1] / 255.), |
| static_cast<GLfloat>(colorData[i + 2] / 255.), static_cast<GLfloat>(colorData[i + 3] / 255.)); |
| } |
| vec4 epsilon = vec4(1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f); // texture format is RGBA8. |
| for (std::size_t i = 0; i < data.size(); ++i) |
| { |
| if (!ColorEqual(data[i], vec4(0.25f, 0.5f, 0.75f, 1.0f), epsilon)) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at texture." << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| } |
| |
| return error; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| for (int i = 0; i < 3; ++i) |
| glDeleteProgram(m_program[i]); |
| glDeleteBuffers(4, m_storage_buffer); |
| glDeleteBuffers(1, &m_counter_buffer); |
| glDeleteTextures(1, &m_texture); |
| glDeleteFramebuffers(1, &m_fbo); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedPipelinePostFS : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "CS as an additional pipeline stage - After FS"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that CS which runs just after FS to do a post-processing on a rendered image works as " |
| "expected." NL "2. Verify that CS used as a post-processing filter works as expected." NL |
| "3. Verify that several CS kernels which run in a sequence to do a post-processing on a rendered " |
| "image works as expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL |
| "1. Render image to Texture0 using VS and FS." NL |
| "2. Use Texture0 as an input to Kernel0 which performs post-processing and writes result to Texture1." NL |
| "3. Issue MemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT) command." NL |
| "4. Use Texture1 as an input to Kernel1 which performs post-processing and writes result to Texture0." NL |
| "5. Issue MemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT) command." NL |
| "6. Verify content of the final post-processed image (Texture0)."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program[3]; |
| GLuint m_render_target[2]; |
| GLuint m_framebuffer; |
| GLuint m_vertex_array; |
| GLuint m_fbo; |
| |
| virtual long Setup() |
| { |
| memset(m_program, 0, sizeof(m_program)); |
| memset(m_render_target, 0, sizeof(m_render_target)); |
| m_framebuffer = 0; |
| m_vertex_array = 0; |
| m_fbo = 0; |
| return NO_ERROR; |
| } |
| |
| virtual long Run() |
| { |
| const char* const glsl_vs = |
| NL "const mediump vec2 g_vertex[4] = vec2[4](vec2(0.0), vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, " |
| "3.0));" NL "void main() {" NL " gl_Position = vec4(g_vertex[gl_VertexID], 0.0, 1.0);" NL "}"; |
| const char* const glsl_fs = NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL |
| " g_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; |
| m_program[0] = CreateProgram(glsl_vs, glsl_fs); |
| glLinkProgram(m_program[0]); |
| if (!CheckProgram(m_program[0])) |
| return ERROR; |
| |
| const char* const glsl_cs = |
| NL "#define TILE_WIDTH 4" NL "#define TILE_HEIGHT 4" NL |
| "const ivec2 kTileSize = ivec2(TILE_WIDTH, TILE_HEIGHT);" NL NL |
| "layout(binding = 0, rgba8) readonly uniform mediump image2D g_input_image;" NL |
| "layout(binding = 1, rgba8) writeonly uniform mediump image2D g_output_image;" NL NL |
| "layout(local_size_x = TILE_WIDTH, local_size_y=TILE_HEIGHT) in;" NL NL "void main() {" NL |
| " ivec2 tile_xy = ivec2(gl_WorkGroupID);" NL " ivec2 thread_xy = ivec2(gl_LocalInvocationID);" NL NL |
| " if (thread_xy == ivec2(0)) {" NL " ivec2 pixel_xy = tile_xy * kTileSize;" NL |
| " for (int y = 0; y < TILE_HEIGHT; ++y) {" NL " for (int x = 0; x < TILE_WIDTH; ++x) {" NL |
| " imageStore(g_output_image, pixel_xy + ivec2(x, y), vec4(0, 1, 0, 1));" NL " }" NL |
| " }" NL " }" NL "}"; |
| |
| m_program[1] = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program[1]); |
| if (!CheckProgram(m_program[1])) |
| return ERROR; |
| |
| const char* const glsl_cs2 = |
| NL "#define TILE_WIDTH 8" NL "#define TILE_HEIGHT 8" NL |
| "const ivec2 kTileSize = ivec2(TILE_WIDTH, TILE_HEIGHT);" NL NL |
| "layout(binding = 0, rgba8) readonly uniform mediump image2D g_input_image;" NL |
| "layout(binding = 1, rgba8) writeonly uniform mediump image2D g_output_image;" NL NL |
| "layout(local_size_x = TILE_WIDTH, local_size_y=TILE_HEIGHT) in;" NL NL "vec4 Process(vec4 ic) {" NL |
| " return ic + vec4(1.0, 0.0, 0.0, 0.0);" NL "}" NL "void main() {" NL |
| " ivec2 tile_xy = ivec2(gl_WorkGroupID);" NL " ivec2 thread_xy = ivec2(gl_LocalInvocationID);" NL |
| " ivec2 pixel_xy = tile_xy * kTileSize + thread_xy;" NL |
| " vec4 ic = imageLoad(g_input_image, pixel_xy);" NL |
| " imageStore(g_output_image, pixel_xy, Process(ic));" NL "}"; |
| |
| m_program[2] = CreateComputeProgram(glsl_cs2); |
| glLinkProgram(m_program[2]); |
| if (!CheckProgram(m_program[2])) |
| return ERROR; |
| |
| glGenVertexArrays(1, &m_vertex_array); |
| |
| /* init render targets */ |
| { |
| std::vector<GLint> data(128 * 128 * 4); |
| glGenTextures(2, m_render_target); |
| for (int i = 0; i < 2; ++i) |
| { |
| glBindTexture(GL_TEXTURE_2D, m_render_target[i]); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 128, 128); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| } |
| glBindTexture(GL_TEXTURE_2D, 0); |
| } |
| |
| glGenFramebuffers(1, &m_framebuffer); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_render_target[0], 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); |
| glUseProgram(m_program[0]); |
| glBindVertexArray(m_vertex_array); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glViewport(0, 0, 128, 128); |
| // draw full-viewport triangle |
| glDrawArrays(GL_TRIANGLES, 1, |
| 3); // note: <first> is 1 this means that gl_VertexID in the VS will be: 1, 2 and 3 |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| glBindImageTexture(0, m_render_target[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); // input |
| glBindImageTexture(1, m_render_target[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); // output |
| glUseProgram(m_program[1]); |
| glDispatchCompute(128 / 4, 128 / 4, 1); |
| |
| glBindImageTexture(0, m_render_target[1], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); // input |
| glBindImageTexture(1, m_render_target[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); // output |
| glUseProgram(m_program[2]); |
| glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); |
| glDispatchCompute(128 / 8, 128 / 8, 1); |
| |
| /* validate render target */ |
| { |
| std::vector<vec4> data(128 * 128); |
| glBindTexture(GL_TEXTURE_2D, m_render_target[0]); |
| glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); |
| glGenFramebuffers(1, &m_fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_render_target[0], 0); |
| std::vector<GLubyte> colorData(128 * 128 * 4); |
| glReadPixels(0, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, &colorData[0]); |
| for (int i = 0; i < 128 * 128 * 4; i += 4) |
| { |
| data[i / 4] = |
| vec4(static_cast<GLfloat>(colorData[i] / 255.), static_cast<GLfloat>(colorData[i + 1] / 255.), |
| static_cast<GLfloat>(colorData[i + 2] / 255.), static_cast<GLfloat>(colorData[i + 3] / 255.)); |
| } |
| for (std::size_t i = 0; i < data.size(); ++i) |
| { |
| if (!IsEqual(data[i], vec4(1, 1, 0, 1))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at index " << i << ": " << data[i].x() << ", " |
| << data[i].y() << ", " << data[i].z() << ", " << data[i].w() << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| } |
| return NO_ERROR; |
| } |
| |
| virtual long Cleanup() |
| { |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| glUseProgram(0); |
| for (int i = 0; i < 3; ++i) |
| glDeleteProgram(m_program[i]); |
| glDeleteTextures(2, m_render_target); |
| glDeleteVertexArrays(1, &m_vertex_array); |
| glDeleteFramebuffers(1, &m_framebuffer); |
| glDeleteFramebuffers(1, &m_fbo); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedPipelinePostXFB : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "CS as an additional pipeline stage - After XFB"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that CS which process data fedback by VS works as expected." NL |
| "2. Verify that XFB and SSBO works correctly together in one shader." NL |
| "3. Verify that 'switch' statment which selects different execution path for each CS thread works as " |
| "expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Draw triangle with XFB enabled. Some data is written to the XFB buffer." NL |
| "2. Use XFB buffer as 'input SSBO' in CS. Process data and write it to 'output SSBO'." NL |
| "3. Verify 'output SSBO' content."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program[2]; |
| GLuint m_storage_buffer; |
| GLuint m_xfb_buffer; |
| GLuint m_vertex_buffer; |
| GLuint m_vertex_array; |
| |
| virtual long Setup() |
| { |
| memset(m_program, 0, sizeof(m_program)); |
| m_storage_buffer = 0; |
| m_xfb_buffer = 0; |
| m_vertex_buffer = 0; |
| m_vertex_array = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| GLint res; |
| glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &res); |
| if (res <= 0) |
| { |
| OutputNotSupported("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS <= 0"); |
| return NOT_SUPPORTED; |
| } |
| |
| const char* const glsl_vs = |
| NL "layout(location = 0) in mediump vec4 g_position;" NL "layout(location = 1) in mediump vec4 g_color;" NL |
| "struct Vertex {" NL " mediump vec4 position;" NL " mediump vec4 color;" NL "};" NL |
| "flat out mediump vec4 color;" NL "layout(binding = 0) buffer StageData {" NL " Vertex vertex[];" NL |
| "} g_vs_buffer;" NL "void main() {" NL " gl_Position = g_position;" NL " color = g_color;" NL |
| " g_vs_buffer.vertex[gl_VertexID].position = g_position;" NL |
| " g_vs_buffer.vertex[gl_VertexID].color = g_color;" NL "}"; |
| const char* const glsl_fs = |
| NL "flat mediump in vec4 color;" NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL |
| " g_color = color;" NL "}"; |
| m_program[0] = CreateProgram(glsl_vs, glsl_fs); |
| /* setup xfb varyings */ |
| { |
| const char* const var[2] = { "gl_Position", "color" }; |
| glTransformFeedbackVaryings(m_program[0], 2, var, GL_INTERLEAVED_ATTRIBS); |
| } |
| glLinkProgram(m_program[0]); |
| if (!CheckProgram(m_program[0])) |
| return ERROR; |
| |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 3) in;" NL "struct Vertex {" NL " vec4 position;" NL " vec4 color;" NL "};" NL |
| "layout(binding = 3, std430) buffer Buffer {" NL " Vertex g_vertex[3];" NL "};" NL |
| "uniform vec4 g_color1;" NL "uniform int g_two;" NL "void UpdateVertex2(int i) {" NL |
| " g_vertex[i].color -= vec4(-1, 1, 0, 0);" NL "}" NL "void main() {" NL |
| " switch (gl_GlobalInvocationID.x) {" NL |
| " case 0u: g_vertex[gl_GlobalInvocationID.x].color += vec4(1, 0, 0, 0); break;" NL |
| " case 1u: g_vertex[1].color += g_color1; break;" NL " case 2u: UpdateVertex2(g_two); break;" NL |
| " default: return;" NL " }" NL "}"; |
| m_program[1] = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program[1]); |
| glUseProgram(m_program[1]); |
| glUniform4f(glGetUniformLocation(m_program[1], "g_color1"), 0.f, 0.f, 1.f, 0.f); |
| glUniform1i(glGetUniformLocation(m_program[1], "g_two"), 2); |
| glUseProgram(0); |
| if (!CheckProgram(m_program[1])) |
| return ERROR; |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * sizeof(vec4) * 2, NULL, GL_STATIC_COPY); |
| |
| glGenBuffers(1, &m_xfb_buffer); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfb_buffer); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 3 * sizeof(vec4) * 2, NULL, GL_STREAM_COPY); |
| |
| const float in_data[3 * 8] = { -1, -1, 0, 1, 0, 1, 0, 1, 3, -1, 0, 1, 0, 1, 0, 1, -1, 3, 0, 1, 0, 1, 0, 1 }; |
| glGenBuffers(1, &m_vertex_buffer); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(in_data), in_data, GL_STATIC_DRAW); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| glGenVertexArrays(1, &m_vertex_array); |
| glBindVertexArray(m_vertex_array); |
| glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); |
| glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 2 * sizeof(vec4), 0); |
| glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 2 * sizeof(vec4), reinterpret_cast<void*>(sizeof(vec4))); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glEnableVertexAttribArray(0); |
| glEnableVertexAttribArray(1); |
| glBindVertexArray(0); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glUseProgram(m_program[0]); |
| glBindVertexArray(m_vertex_array); |
| glBeginTransformFeedback(GL_TRIANGLES); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| glEndTransformFeedback(); |
| |
| glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); |
| |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_xfb_buffer); |
| glUseProgram(m_program[1]); |
| glDispatchCompute(1, 1, 1); |
| |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| long error = NO_ERROR; |
| /* validate storage buffer */ |
| { |
| float* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| data = static_cast<float*>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(float) * 3 * 8, GL_MAP_READ_BIT)); |
| if (memcmp(data, in_data, sizeof(float) * 3 * 8) != 0) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data in shader storage buffer is incorrect." |
| << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| } |
| /* validate xfb buffer */ |
| { |
| const float ref_data[3 * 8] = { |
| -1, -1, 0, 1, 1, 1, 0, 1, 3, -1, 0, 1, 0, 1, 1, 1, -1, 3, 0, 1, 1, 0, 0, 1 |
| }; |
| float* data; |
| data = static_cast<float*>( |
| glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(float) * 3 * 8, GL_MAP_READ_BIT)); |
| if (memcmp(data, ref_data, sizeof(float) * 3 * 8) != 0) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Data in xfb buffer is incorrect." << tcu::TestLog::EndMessage; |
| error = ERROR; |
| } |
| glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); |
| } |
| if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) |
| { |
| error = ERROR; |
| } |
| return error; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| for (int i = 0; i < 2; ++i) |
| glDeleteProgram(m_program[i]); |
| glDeleteBuffers(1, &m_vertex_buffer); |
| glDeleteBuffers(1, &m_storage_buffer); |
| glDeleteBuffers(1, &m_xfb_buffer); |
| glDeleteVertexArrays(1, &m_vertex_array); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedSharedIndexing : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Shared Memory - Indexing"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that indexing various types of shared memory works as expected." NL |
| "2. Verify that indexing shared memory with different types of expressions work as expected." NL |
| "3. Verify that all declaration types of shared structures are supported by the GLSL compiler."; |
| } |
| virtual std::string Method() |
| { |
| return NL "1. Create CS which uses shared memory in many different ways." NL |
| "2. Write to shared memory using different expressions." NL "3. Validate shared memory content." NL |
| "4. Use synchronization primitives (barrier, groupMemoryBarrier) where applicable."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everyting works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_texture; |
| GLuint m_fbo; |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_texture = 0; |
| m_fbo = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "layout(binding = 3, rgba8) uniform mediump writeonly image2D g_result_image;" NL |
| "layout (local_size_x = 4,local_size_y=4 ) in;" NL "shared vec4 g_shared1[4];" NL |
| "shared mat4 g_shared2;" NL "shared struct {" NL " float data[4];" NL "} g_shared3[4];" NL |
| "shared struct Type { float data[4]; } g_shared4[4];" NL "shared Type g_shared5[4];" NL |
| "uniform bool g_true;" NL "uniform float g_values[16];" NL NL "void Sync() {" NL |
| " groupMemoryBarrier();" NL " barrier();" NL "}" NL "void SetMemory(ivec2 xy, float value) {" NL |
| " g_shared1[xy.y][gl_LocalInvocationID.x] = value;" NL " g_shared2[xy.y][xy.x] = value;" NL |
| " g_shared3[xy[1]].data[xy[0]] = value;" NL " g_shared4[xy.y].data[xy[0]] = value;" NL |
| " g_shared5[gl_LocalInvocationID.y].data[gl_LocalInvocationID.x] = value;" NL "}" NL |
| "bool CheckMemory(ivec2 xy, float expected) {" NL |
| " if (g_shared1[xy.y][xy[0]] != expected) return false;" NL |
| " if (g_shared2[xy[1]][xy[0]] != expected) return false;" NL |
| " if (g_shared3[gl_LocalInvocationID.y].data[gl_LocalInvocationID.x] != expected) return false;" NL |
| " if (g_shared4[gl_LocalInvocationID.y].data[xy.x] != expected) return false;" NL |
| " if (g_shared5[xy.y].data[xy.x] != expected) return false;" NL " return true;" NL "}" NL |
| "void main() {" NL " ivec2 thread_xy = ivec2(gl_LocalInvocationID);" NL |
| " vec4 result = vec4(0.0, 1.0, 0.0, 1.0);" NL NL |
| " SetMemory(thread_xy, g_values[gl_LocalInvocationIndex] * 1.0);" NL " Sync();" NL |
| " if (!CheckMemory(thread_xy, g_values[gl_LocalInvocationIndex] * 1.0)) result = vec4(1.0, 0.0, 0.0, " |
| "1.0);" NL NL " SetMemory(thread_xy, g_values[gl_LocalInvocationIndex] * -1.0);" NL " Sync();" NL |
| " if (!CheckMemory(thread_xy, g_values[gl_LocalInvocationIndex] * -1.0)) result = vec4(1.0, 0.0, 0.0, " |
| "1.0);" NL NL " if (g_true && gl_LocalInvocationID.x < 10u) {" NL |
| " SetMemory(thread_xy, g_values[gl_LocalInvocationIndex] * 7.0);" NL " Sync();" NL |
| " if (!CheckMemory(thread_xy, g_values[gl_LocalInvocationIndex] * 7.0)) result = vec4(1.0, 0.0, 0.0, " |
| "1.0);" NL " }" NL NL " imageStore(g_result_image, thread_xy, result);" NL "}"; |
| |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| /* init texture */ |
| { |
| std::vector<GLint> data(4 * 4 * 4); |
| glGenTextures(1, &m_texture); |
| glBindTexture(GL_TEXTURE_2D, m_texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| } |
| |
| glBindImageTexture(3, m_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); |
| glUseProgram(m_program); |
| glUniform1i(glGetUniformLocation(m_program, "g_true"), GL_TRUE); |
| GLfloat values[16] = { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f }; |
| glUniform1fv(glGetUniformLocation(m_program, "g_values"), 16, values); |
| glDispatchCompute(1, 1, 1); |
| |
| /* validate render target */ |
| { |
| std::vector<vec4> data(4 * 4); |
| glBindTexture(GL_TEXTURE_2D, m_texture); |
| glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); |
| glGenFramebuffers(1, &m_fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0); |
| std::vector<GLubyte> colorData(4 * 4 * 4); |
| glReadPixels(0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, &colorData[0]); |
| for (int i = 0; i < 4 * 4 * 4; i += 4) |
| { |
| data[i / 4] = |
| vec4(static_cast<GLfloat>(colorData[i] / 255.), static_cast<GLfloat>(colorData[i + 1] / 255.), |
| static_cast<GLfloat>(colorData[i + 2] / 255.), static_cast<GLfloat>(colorData[i + 3] / 255.)); |
| } |
| for (std::size_t i = 0; i < data.size(); ++i) |
| { |
| if (!IsEqual(data[i], vec4(0, 1, 0, 1))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at index " << i << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| } |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| glDeleteProgram(m_program); |
| glDeleteTextures(1, &m_texture); |
| glDeleteFramebuffers(1, &m_fbo); |
| return NO_ERROR; |
| } |
| }; |
| |
| class AdvancedSharedMax : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Shared Memory - 16K"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Support for 16K of shared memory is required by the OpenGL specifaction. Verify if an " |
| "implementation supports it."; |
| } |
| virtual std::string Method() |
| { |
| return NL "Create and dispatch CS which uses 16K of shared memory."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_buffer; |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_buffer = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = NL |
| "layout(local_size_x = 64) in;" NL |
| "shared struct Type { vec4 v[16]; } g_shared[64];" // 16384 bytes of shared memory |
| NL "layout(std430) buffer Output {" NL " Type g_output[64];" NL "};" NL NL "void main() {" NL |
| " int id = int(gl_GlobalInvocationID.x);" NL |
| " g_shared[id].v = vec4[16](vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), " |
| "vec4(1.0)," NL " vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), vec4(1.0), " |
| "vec4(1.0), vec4(1.0), vec4(1.0));" NL " memoryBarrierShared();" NL " barrier();" NL NL |
| " vec4 sum = vec4(0.0);" NL " int sum_count = 0;" NL " for (int i = id - 6; i < id + 9; ++i) {" NL |
| " if (id >= 0 && id < g_shared.length()) {" NL " sum += g_shared[id].v[0];" NL |
| " sum += g_shared[id].v[1];" NL " sum += g_shared[id].v[2];" NL |
| " sum += g_shared[id].v[3];" NL " sum += g_shared[id].v[4];" NL |
| " sum += g_shared[id].v[5];" NL " sum += g_shared[id].v[6];" NL |
| " sum += g_shared[id].v[7];" NL " sum += g_shared[id].v[8];" NL |
| " sum += g_shared[id].v[9];" NL " sum += g_shared[id].v[10];" NL |
| " sum += g_shared[id].v[11];" NL " sum += g_shared[id].v[12];" NL |
| " sum += g_shared[id].v[13];" NL " sum += g_shared[id].v[14];" NL |
| " sum += g_shared[id].v[15];" NL " sum_count += 16;" NL " }" NL " }" NL |
| " sum = abs((sum / float(sum_count)) - vec4(1.0));" NL |
| " if (sum.x > 0.0000001f || sum.y > 0.0000001f || sum.z > 0.0000001f || sum.w > 0.0000001f) return;" NL NL |
| " g_output[id] = g_shared[id];" NL "}"; |
| |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| /* init buffer */ |
| { |
| std::vector<vec4> data(1024); |
| glGenBuffers(1, &m_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)(sizeof(vec4) * data.size()), &data[0][0], |
| GL_DYNAMIC_COPY); |
| } |
| |
| glUseProgram(m_program); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| long error = NO_ERROR; |
| /* validate buffer */ |
| { |
| vec4* data; |
| data = |
| static_cast<vec4*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(vec4) * 1024, GL_MAP_READ_BIT)); |
| for (std::size_t i = 0; i < 1024; ++i) |
| { |
| if (!IsEqual(data[i], vec4(1.0f))) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid data at index " << i << tcu::TestLog::EndMessage; |
| 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 AdvancedResourcesMax : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Maximum number of resources in one shader"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "1. Verify that using 4 SSBOs, 12 UBOs, 8 atomic counters" NL " in one CS works as expected."; |
| } |
| virtual std::string Method() |
| { |
| return NL "Create and dispatch CS. Verify result."; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL "Everything works as expected."; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer[4]; |
| GLuint m_uniform_buffer[12]; |
| GLuint m_atomic_buffer; |
| |
| bool RunIteration(GLuint index) |
| { |
| for (GLuint i = 0; i < 4; ++i) |
| { |
| const GLuint data = i + 1; |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, m_storage_buffer[i]); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), &data, GL_STATIC_DRAW); |
| } |
| for (GLuint i = 0; i < 12; ++i) |
| { |
| const GLuint data = i + 1; |
| glBindBufferBase(GL_UNIFORM_BUFFER, i, m_uniform_buffer[i]); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(data), &data, GL_STATIC_DRAW); |
| } |
| { |
| GLuint data[8]; |
| for (GLuint i = 0; i < 8; ++i) |
| data[i] = i + 1; |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_atomic_buffer); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(data), &data[0], GL_STATIC_DRAW); |
| } |
| |
| glUseProgram(m_program); |
| glUniform1ui(glGetUniformLocation(m_program, "g_index"), index); |
| /* uniform array */ |
| { |
| std::vector<GLuint> data(480); |
| for (GLuint i = 0; i < static_cast<GLuint>(data.size()); ++i) |
| data[i] = i + 1; |
| glUniform1uiv(glGetUniformLocation(m_program, "g_uniform_def"), static_cast<GLsizei>(data.size()), |
| &data[0]); |
| } |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| bool ret = true; |
| /* validate buffer */ |
| { |
| GLuint* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer[index]); |
| data = static_cast<GLuint*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), GL_MAP_READ_BIT)); |
| if (data[0] != (index + 1) * 4) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data is " << data[0] << " should be " |
| << ((index + 1) * 4) << tcu::TestLog::EndMessage; |
| ret = false; |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| return ret; |
| } |
| virtual long Setup() |
| { |
| m_program = 0; |
| memset(m_storage_buffer, 0, sizeof(m_storage_buffer)); |
| memset(m_uniform_buffer, 0, sizeof(m_uniform_buffer)); |
| m_atomic_buffer = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 1) in;" NL "layout(std140, binding = 0) buffer ShaderStorageBlock {" NL |
| " uint data;" NL "} g_shader_storage[4];" NL "layout(std140, binding = 0) uniform UniformBlock {" NL |
| " uint data;" NL "} g_uniform[12];" NL |
| "layout(binding = 0, offset = 0) uniform atomic_uint g_atomic_counter0;" NL |
| "layout(binding = 0, offset = 4) uniform atomic_uint g_atomic_counter1;" NL |
| "layout(binding = 0, offset = 8) uniform atomic_uint g_atomic_counter2;" NL |
| "layout(binding = 0, offset = 12) uniform atomic_uint g_atomic_counter3;" NL |
| "layout(binding = 0, offset = 16) uniform atomic_uint g_atomic_counter4;" NL |
| "layout(binding = 0, offset = 20) uniform atomic_uint g_atomic_counter5;" NL |
| "layout(binding = 0, offset = 24) uniform atomic_uint g_atomic_counter6;" NL |
| "layout(binding = 0, offset = 28) uniform atomic_uint g_atomic_counter7;" NL |
| "uniform uint g_uniform_def[480];" NL "uniform uint g_index;" NL NL "uint Add() {" NL |
| " switch (g_index) {" NL " case 0u: return atomicCounter(g_atomic_counter0);" NL |
| " case 1u: return atomicCounter(g_atomic_counter1);" NL |
| " case 2u: return atomicCounter(g_atomic_counter2);" NL |
| " case 3u: return atomicCounter(g_atomic_counter3);" NL |
| " case 4u: return atomicCounter(g_atomic_counter4);" NL |
| " case 5u: return atomicCounter(g_atomic_counter5);" NL |
| " case 6u: return atomicCounter(g_atomic_counter6);" NL |
| " case 7u: return atomicCounter(g_atomic_counter7);" NL " }" NL "}" NL "void main() {" NL |
| " switch (g_index) {" NL " case 0u: {" NL " g_shader_storage[0].data += g_uniform[0].data;" NL |
| " g_shader_storage[0].data += Add();" NL " g_shader_storage[0].data += g_uniform_def[0];" NL |
| " break;" NL " }" NL " case 1u: {" NL |
| " g_shader_storage[1].data += g_uniform[1].data;" NL " g_shader_storage[1].data += Add();" NL |
| " g_shader_storage[1].data += g_uniform_def[1];" NL " break;" NL " }" NL " case 2u: {" NL |
| " g_shader_storage[2].data += g_uniform[2].data;" NL " g_shader_storage[2].data += Add();" NL |
| " g_shader_storage[2].data += g_uniform_def[2];" NL " break;" NL " }" NL " case 3u: {" NL |
| " g_shader_storage[3].data += g_uniform[3].data;" NL " g_shader_storage[3].data += Add();" NL |
| " g_shader_storage[3].data += g_uniform_def[3];" NL " break;" NL " }" NL " }" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glGenBuffers(4, m_storage_buffer); |
| glGenBuffers(12, m_uniform_buffer); |
| glGenBuffers(1, &m_atomic_buffer); |
| |
| if (!RunIteration(0)) |
| return ERROR; |
| if (!RunIteration(1)) |
| return ERROR; |
| if (!RunIteration(3)) |
| return ERROR; |
| |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| glDeleteProgram(m_program); |
| glDeleteBuffers(4, m_storage_buffer); |
| glDeleteBuffers(12, m_uniform_buffer); |
| glDeleteBuffers(1, &m_atomic_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| class WorkGroupSizeUsage : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "gl_WorkGroupSize usage"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify gl_WorkGroupSize usage rules."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| virtual long Run() |
| { |
| // local group size declared with some dimensions omitted - omitted dimensions should have size of 1 |
| if (!CheckOmittedDimensions('x') || !CheckOmittedDimensions('y') || !CheckOmittedDimensions('z')) |
| return ERROR; |
| |
| // check if compilation error is generated when shader doesn't declare |
| // fixed local group size and tries to use gl_WorkGroupSize |
| if (!CheckCompilationError("#version 310 es" NL "layout(std430) buffer Output {" NL " uint g_output;" NL |
| "};" NL "void main() {" NL " g_output = gl_WorkGroupSize.x;" NL "}")) |
| return ERROR; |
| |
| // check if compilation error is generated when shader tries using |
| // gl_WorkGroupSize in a function before declaring local group size |
| if (!CheckCompilationError("#version 310 es" NL "layout(std430) buffer Output {" NL " uint g_output;" NL |
| "};" NL "void main() {" NL " g_output = gl_WorkGroupSize.x;" NL "}" NL |
| "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;")) |
| return ERROR; |
| |
| return NO_ERROR; |
| } |
| |
| bool CheckOmittedDimensions(GLchar defined_component) |
| { |
| std::stringstream ss; |
| ss << "layout(std430) buffer Output {" NL " uint g_output;" NL "};" NL "layout(local_size_" |
| << defined_component |
| << " = 1) in;" NL "void main() {" NL " g_output = gl_WorkGroupSize.x + gl_WorkGroupSize.z;" NL "}"; |
| |
| std::string glsl_cs = ss.str(); |
| GLuint program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(program); |
| if (!CheckProgram(program)) |
| return false; |
| |
| GLint v[3]; |
| glGetProgramiv(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 false; |
| } |
| |
| glDeleteProgram(program); |
| return true; |
| } |
| |
| bool CheckCompilationError(const std::string& source) |
| { |
| const GLuint sh = glCreateShader(GL_COMPUTE_SHADER); |
| |
| const char* const src = source.c_str(); |
| glShaderSource(sh, 1, &src, NULL); |
| glCompileShader(sh); |
| |
| GLchar log[1024]; |
| glGetShaderInfoLog(sh, sizeof(log), NULL, log); |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader Info Log:\n" |
| << log << tcu::TestLog::EndMessage; |
| |
| GLint status; |
| glGetShaderiv(sh, GL_COMPILE_STATUS, &status); |
| glDeleteShader(sh); |
| |
| if (status == GL_TRUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Compilation should fail." << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| return true; |
| } |
| }; |
| |
| class NegativeAPINoActiveProgram : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "API errors - no active program"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the OpenGL API."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| GLuint m_program; |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| return NO_ERROR; |
| } |
| virtual long Run() |
| { |
| glDispatchCompute(1, 2, 3); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_OPERATION is generated by DispatchCompute or\n" |
| "DispatchComputeIndirect if there is no active program for the compute\n" |
| "shader stage." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| /* indirect dispatch */ |
| { |
| GLuint buffer; |
| const GLuint num_group[3] = { 3, 2, 1 }; |
| glGenBuffers(1, &buffer); |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer); |
| glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_group), num_group, GL_STATIC_DRAW); |
| glDispatchComputeIndirect(0); |
| glDeleteBuffers(1, &buffer); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "INVALID_OPERATION is generated by DispatchCompute or\n" |
| "DispatchComputeIndirect if there is no active program for the compute\n" |
| "shader stage." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| |
| const char* const glsl_vs = NL "layout(location = 0) in mediump vec4 g_position;" NL "void main() {" NL |
| " gl_Position = g_position;" NL "}"; |
| const char* const glsl_fs = |
| NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL " g_color = vec4(1);" NL "}"; |
| m_program = CreateProgram(glsl_vs, glsl_fs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glUseProgram(m_program); |
| |
| glDispatchCompute(1, 2, 3); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_OPERATION is generated by DispatchCompute or\n" |
| "DispatchComputeIndirect if there is no active program for the compute\n" |
| "shader stage." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| /* indirect dispatch */ |
| { |
| GLuint buffer; |
| const GLuint num_group[3] = { 3, 2, 1 }; |
| glGenBuffers(1, &buffer); |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer); |
| glBufferData(GL_DISPATCH_INDIRECT_BUFFER, sizeof(num_group), num_group, GL_STATIC_DRAW); |
| glDispatchComputeIndirect(0); |
| glDeleteBuffers(1, &buffer); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "INVALID_OPERATION is generated by DispatchCompute or\n" |
| "DispatchComputeIndirect if there is no active program for the compute\n" |
| "shader stage." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| virtual long Cleanup() |
| { |
| glUseProgram(0); |
| glDeleteProgram(m_program); |
| return NO_ERROR; |
| } |
| }; |
| |
| class NegativeAPIWorkGroupCount : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "API errors - invalid work group count"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the OpenGL API."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| 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 "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL |
| "void main() {" NL |
| " g_output[gl_GlobalInvocationID.x * gl_GlobalInvocationID.y * gl_GlobalInvocationID.z] = 0u;" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 100000, NULL, GL_DYNAMIC_DRAW); |
| |
| GLint x, y, z; |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &x); |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &y); |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &z); |
| |
| glUseProgram(m_program); |
| |
| glDispatchCompute(x + 1, 1, 1); |
| if (glGetError() != GL_INVALID_VALUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_VALUE is generated by DispatchCompute if any of <num_groups_x>,\n" |
| "<num_groups_y> or <num_groups_z> is greater than the value of\n" |
| "MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding dimension." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glDispatchCompute(1, y + 1, 1); |
| if (glGetError() != GL_INVALID_VALUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_VALUE is generated by DispatchCompute if any of <num_groups_x>,\n" |
| "<num_groups_y> or <num_groups_z> is greater than the value of\n" |
| "MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding dimension." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glDispatchCompute(1, 1, z + 1); |
| if (glGetError() != GL_INVALID_VALUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_VALUE is generated by DispatchCompute if any of <num_groups_x>,\n" |
| "<num_groups_y> or <num_groups_z> is greater than the value of\n" |
| "MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding dimension." |
| << 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 NegativeAPIIndirect : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "API errors - incorrect DispatchComputeIndirect usage"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the OpenGL API."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| GLuint m_program; |
| GLuint m_storage_buffer; |
| GLuint m_dispatch_buffer; |
| |
| virtual long Setup() |
| { |
| m_program = 0; |
| m_storage_buffer = 0; |
| m_dispatch_buffer = 0; |
| return NO_ERROR; |
| } |
| |
| virtual long Run() |
| { |
| const char* const glsl_cs = |
| NL "layout(local_size_x = 1) in;" NL "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL |
| "void main() {" NL " g_output[gl_GlobalInvocationID.x] = 0u;" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| glUseProgram(m_program); |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 100000, NULL, GL_DYNAMIC_DRAW); |
| |
| const GLuint num_groups[6] = { 1, 1, 1, 1, 1, 1 }; |
| 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_COPY); |
| |
| glDispatchComputeIndirect(-2); |
| if (glGetError() != GL_INVALID_VALUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_VALUE is generated by DispatchComputeIndirect if <indirect> is\n" |
| "less than zero or not a multiple of four." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glDispatchComputeIndirect(3); |
| if (glGetError() != GL_INVALID_VALUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_VALUE is generated by DispatchComputeIndirect if <indirect> is\n" |
| "less than zero or not a multiple of four." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glDispatchComputeIndirect(16); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "INVALID_OPERATION is generated by DispatchComputeIndirect if no buffer is\n" |
| "bound to DISPATCH_INDIRECT_BUFFER or if the command would source data\n" |
| "beyond the end of the bound buffer object." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0); |
| glDispatchComputeIndirect(0); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message |
| << "INVALID_OPERATION is generated by DispatchComputeIndirect if no buffer is\n" |
| "bound to DISPATCH_INDIRECT_BUFFER or if the command would source data\n" |
| "beyond the end of the bound buffer object." |
| << tcu::TestLog::EndMessage; |
| 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 NegativeAPIProgram : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "API errors - program state"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the OpenGL API."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| 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_vs = NL "layout(location = 0) in mediump vec4 g_position;" NL "void main() {" NL |
| " gl_Position = g_position;" NL "}"; |
| const char* const glsl_fs = |
| NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL " g_color = vec4(1);" NL "}"; |
| m_program = CreateProgram(glsl_vs, glsl_fs); |
| |
| GLint v[3]; |
| glGetProgramiv(m_program, GL_COMPUTE_WORK_GROUP_SIZE, v); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_OPERATION is generated by GetProgramiv if <pname> is\n" |
| "COMPUTE_LOCAL_WORK_SIZE and either the program has not been linked\n" |
| "successfully, or has been linked but contains no compute shaders." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glGetProgramiv(m_program, GL_COMPUTE_WORK_GROUP_SIZE, v); |
| if (glGetError() != GL_INVALID_OPERATION) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "INVALID_OPERATION is generated by GetProgramiv if <pname> is\n" |
| "COMPUTE_LOCAL_WORK_SIZE and either the program has not been linked\n" |
| "successfully, or has been linked but contains no compute shaders." |
| << tcu::TestLog::EndMessage; |
| return ERROR; |
| } |
| glDeleteProgram(m_program); |
| |
| const char* const glsl_cs = |
| "#version 310 es" NL "layout(local_size_x = 1) in;" NL "layout(std430) buffer Output {" NL |
| " uint g_output[];" NL "};" NL "void main() {" NL " g_output[gl_GlobalInvocationID.x] = 0;" NL "}"; |
| m_program = glCreateProgram(); |
| |
| GLuint sh = glCreateShader(GL_COMPUTE_SHADER); |
| glAttachShader(m_program, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_cs, NULL); |
| glCompileShader(sh); |
| |
| sh = glCreateShader(GL_VERTEX_SHADER); |
| glAttachShader(m_program, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_vs, NULL); |
| glCompileShader(sh); |
| |
| sh = glCreateShader(GL_FRAGMENT_SHADER); |
| glAttachShader(m_program, sh); |
| glDeleteShader(sh); |
| glShaderSource(sh, 1, &glsl_fs, NULL); |
| glCompileShader(sh); |
| |
| glLinkProgram(m_program); |
| GLint status; |
| glGetProgramiv(m_program, GL_LINK_STATUS, &status); |
| if (status == GL_TRUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "LinkProgram will fail if <program> contains a combination of compute and\n" |
| "non-compute shaders." |
| << 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 NegativeGLSLCompileTimeErrors : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Compile-time errors"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the GLSL compiler."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| static std::string Shader1(int x, int y, int z) |
| { |
| std::stringstream ss; |
| ss << "#version 310 es" NL "layout(local_size_x = " << x << ", local_size_y = " << y << ", local_size_z = " << z |
| << ") in;" NL "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL "void main() {" NL |
| " g_output[gl_GlobalInvocationID.x] = 0;" NL "}"; |
| return ss.str(); |
| } |
| virtual long Run() |
| { |
| // gl_GlobalInvocationID requires "#version 310" or later |
| if (!Compile("#version 300 es" NL "layout(local_size_x = 1) in;" NL "layout(std430) buffer Output {" NL |
| " uint g_output[];" NL "};" NL "void main() {" NL " g_output[gl_GlobalInvocationID.x] = 0;" NL |
| "}")) |
| return ERROR; |
| |
| if (!Compile("#version 310 es" NL "layout(local_size_x = 1) in;" NL "layout(local_size_x = 2) in;" NL |
| "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL "void main() {" NL |
| " g_output[gl_GlobalInvocationID.x] = 0;" NL "}")) |
| return ERROR; |
| |
| if (!Compile("#version 310 es" NL "layout(local_size_x = 1) in;" NL "in uint x;" NL |
| "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL "void main() {" NL |
| " g_output[gl_GlobalInvocationID.x] = x;" NL "}")) |
| return ERROR; |
| |
| if (!Compile("#version 310 es" NL "layout(local_size_x = 1) in;" NL "out uint x;" NL |
| "layout(std430) buffer Output {" NL " uint g_output[];" NL "};" NL "void main() {" NL |
| " g_output[gl_GlobalInvocationID.x] = 0;" NL " x = 0;" NL "}")) |
| return ERROR; |
| |
| { |
| GLint x, y, z; |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &x); |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &y); |
| glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &z); |
| |
| if (!Compile(Shader1(x + 1, 1, 1))) |
| return ERROR; |
| if (!Compile(Shader1(1, y + 1, 1))) |
| return ERROR; |
| if (!Compile(Shader1(1, 1, z + 1))) |
| return ERROR; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| bool Compile(const std::string& source) |
| { |
| const GLuint sh = glCreateShader(GL_COMPUTE_SHADER); |
| |
| const char* const src = source.c_str(); |
| glShaderSource(sh, 1, &src, NULL); |
| glCompileShader(sh); |
| |
| GLchar log[1024]; |
| glGetShaderInfoLog(sh, sizeof(log), NULL, log); |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader Info Log:\n" |
| << log << tcu::TestLog::EndMessage; |
| |
| GLint status; |
| glGetShaderiv(sh, GL_COMPILE_STATUS, &status); |
| glDeleteShader(sh); |
| |
| if (status == GL_TRUE) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Compilation should fail." << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| return true; |
| } |
| }; |
| |
| class NegativeGLSLLinkTimeErrors : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "Link-time errors"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that appropriate errors are generated by the GLSL linker."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| virtual long Run() |
| { |
| 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 "}"; |
| const char* const glsl_vs = NL "layout(location = 0) in mediump vec4 g_position;" NL "void main() {" NL |
| " gl_Position = g_position;" NL "}"; |
| const char* const glsl_fs = |
| NL "layout(location = 0) out mediump vec4 g_color;" NL "void main() {" NL " g_color = vec4(1);" NL "}"; |
| |
| GLuint p = CreateComputeProgram(glsl_cs); |
| |
| { |
| const GLuint sh = glCreateShader(GL_VERTEX_SHADER); |
| glAttachShader(p, sh); |
| glDeleteShader(sh); |
| const char* const src[2] = { kGLSLVer, glsl_vs }; |
| glShaderSource(sh, 2, src, NULL); |
| glCompileShader(sh); |
| } |
| { |
| const GLuint sh = glCreateShader(GL_FRAGMENT_SHADER); |
| glAttachShader(p, sh); |
| glDeleteShader(sh); |
| const char* const src[2] = { kGLSLVer, glsl_fs }; |
| glShaderSource(sh, 2, src, NULL); |
| glCompileShader(sh); |
| } |
| long error = NO_ERROR; |
| glLinkProgram(p); |
| if (CheckProgram(p)) |
| error = ERROR; |
| |
| /* no layout */ |
| const char* const glsl_cs2 = 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 "}"; |
| |
| GLuint p2 = CreateComputeProgram(glsl_cs2); |
| glLinkProgram(p2); |
| if (CheckProgram(p2)) |
| error = ERROR; |
| |
| glDeleteProgram(p); |
| glDeleteProgram(p2); |
| return error; |
| } |
| }; |
| |
| class BasicWorkGroupSizeIsConst : public ComputeShaderBase |
| { |
| virtual std::string Title() |
| { |
| return NL "gl_WorkGroupSize is an constant"; |
| } |
| virtual std::string Purpose() |
| { |
| return NL "Verify that gl_WorkGroupSize can be used as an constant expression."; |
| } |
| virtual std::string Method() |
| { |
| return NL ""; |
| } |
| virtual std::string PassCriteria() |
| { |
| return NL ""; |
| } |
| |
| 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 = 2, local_size_y = 3, local_size_z = 4) in;" NL |
| "layout(std430, binding = 0) buffer Output {" NL " uint g_buffer[22u + gl_WorkGroupSize.x];" NL "};" NL |
| "shared uint g_shared[gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z];" NL |
| "uniform uint g_uniform[gl_WorkGroupSize.z + 20u];" NL "void main() {" NL |
| " g_shared[gl_LocalInvocationIndex] = 1U;" NL " groupMemoryBarrier();" NL " barrier();" NL |
| " uint sum = 0u;" NL |
| " for (uint i = 0u; i < gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z; ++i) {" NL |
| " sum += g_shared[i];" NL " }" NL " sum += g_uniform[gl_LocalInvocationIndex];" NL |
| " g_buffer[gl_LocalInvocationIndex] = sum;" NL "}"; |
| m_program = CreateComputeProgram(glsl_cs); |
| glLinkProgram(m_program); |
| if (!CheckProgram(m_program)) |
| return ERROR; |
| |
| glGenBuffers(1, &m_storage_buffer); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storage_buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 24 * sizeof(GLuint), NULL, GL_STATIC_DRAW); |
| |
| glUseProgram(m_program); |
| GLuint values[24] = { 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, |
| 13u, 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u }; |
| glUniform1uiv(glGetUniformLocation(m_program, "g_uniform"), 24, values); |
| glDispatchCompute(1, 1, 1); |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| long error = NO_ERROR; |
| GLuint* data; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_storage_buffer); |
| data = |
| static_cast<GLuint*>(glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint) * 24, GL_MAP_READ_BIT)); |
| for (GLuint i = 0; i < 24; ++i) |
| { |
| if (data[i] != (i + 25)) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Data at index " << i << " is " |
| << data[i] << " should be" << (i + 25) << tcu::TestLog::EndMessage; |
| 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_storage_buffer); |
| return NO_ERROR; |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| ComputeShaderTests::ComputeShaderTests(glcts::Context& context) : TestCaseGroup(context, "compute_shader", "") |
| { |
| } |
| |
| ComputeShaderTests::~ComputeShaderTests(void) |
| { |
| } |
| |
| void ComputeShaderTests::init() |
| { |
| using namespace glcts; |
| addChild(new TestSubcase(m_context, "simple-compute", TestSubcase::Create<SimpleCompute>)); |
| addChild(new TestSubcase(m_context, "one-work-group", TestSubcase::Create<BasicOneWorkGroup>)); |
| addChild(new TestSubcase(m_context, "resource-ubo", TestSubcase::Create<BasicResourceUBO>)); |
| addChild(new TestSubcase(m_context, "resource-texture", TestSubcase::Create<BasicResourceTexture>)); |
| addChild(new TestSubcase(m_context, "resource-image", TestSubcase::Create<BasicResourceImage>)); |
| addChild(new TestSubcase(m_context, "resource-atomic-counter", TestSubcase::Create<BasicResourceAtomicCounter>)); |
| addChild(new TestSubcase(m_context, "resource-uniform", TestSubcase::Create<BasicResourceUniform>)); |
| addChild(new TestSubcase(m_context, "built-in-variables", TestSubcase::Create<BasicBuiltinVariables>)); |
| addChild(new TestSubcase(m_context, "max", TestSubcase::Create<BasicMax>)); |
| addChild(new TestSubcase(m_context, "work-group-size", TestSubcase::Create<BasicWorkGroupSizeIsConst>)); |
| addChild(new TestSubcase(m_context, "build-separable", TestSubcase::Create<BasicBuildSeparable>)); |
| addChild(new TestSubcase(m_context, "shared-simple", TestSubcase::Create<BasicSharedSimple>)); |
| addChild(new TestSubcase(m_context, "shared-struct", TestSubcase::Create<BasicSharedStruct>)); |
| addChild(new TestSubcase(m_context, "dispatch-indirect", TestSubcase::Create<BasicDispatchIndirect>)); |
| addChild(new TestSubcase(m_context, "sso-compute-pipeline", TestSubcase::Create<BasicSSOComputePipeline>)); |
| addChild(new TestSubcase(m_context, "sso-case2", TestSubcase::Create<BasicSSOCase2>)); |
| addChild(new TestSubcase(m_context, "sso-case3", TestSubcase::Create<BasicSSOCase3>)); |
| addChild(new TestSubcase(m_context, "atomic-case1", TestSubcase::Create<BasicAtomicCase1>)); |
| addChild(new TestSubcase(m_context, "atomic-case2", TestSubcase::Create<BasicAtomicCase2>)); |
| addChild(new TestSubcase(m_context, "atomic-case3", TestSubcase::Create<BasicAtomicCase3>)); |
| addChild(new TestSubcase(m_context, "copy-image", TestSubcase::Create<AdvancedCopyImage>)); |
| addChild(new TestSubcase(m_context, "pipeline-pre-vs", TestSubcase::Create<AdvancedPipelinePreVS>)); |
| addChild( |
| new TestSubcase(m_context, "pipeline-gen-draw-commands", TestSubcase::Create<AdvancedPipelineGenDrawCommands>)); |
| addChild(new TestSubcase(m_context, "pipeline-compute-chain", TestSubcase::Create<AdvancedPipelineComputeChain>)); |
| addChild(new TestSubcase(m_context, "pipeline-post-fs", TestSubcase::Create<AdvancedPipelinePostFS>)); |
| addChild(new TestSubcase(m_context, "pipeline-post-xfb", TestSubcase::Create<AdvancedPipelinePostXFB>)); |
| addChild(new TestSubcase(m_context, "shared-indexing", TestSubcase::Create<AdvancedSharedIndexing>)); |
| addChild(new TestSubcase(m_context, "shared-max", TestSubcase::Create<AdvancedSharedMax>)); |
| addChild(new TestSubcase(m_context, "resources-max", TestSubcase::Create<AdvancedResourcesMax>)); |
| addChild(new TestSubcase(m_context, "work-group-size-usage", TestSubcase::Create<WorkGroupSizeUsage>)); |
| addChild(new TestSubcase(m_context, "api-no-active-program", TestSubcase::Create<NegativeAPINoActiveProgram>)); |
| addChild(new TestSubcase(m_context, "api-work-group-count", TestSubcase::Create<NegativeAPIWorkGroupCount>)); |
| addChild(new TestSubcase(m_context, "api-indirect", TestSubcase::Create<NegativeAPIIndirect>)); |
| addChild(new TestSubcase(m_context, "api-program", TestSubcase::Create<NegativeAPIProgram>)); |
| addChild( |
| new TestSubcase(m_context, "glsl-compile-time-errors", TestSubcase::Create<NegativeGLSLCompileTimeErrors>)); |
| addChild(new TestSubcase(m_context, "glsl-link-time-errors", TestSubcase::Create<NegativeGLSLLinkTimeErrors>)); |
| addChild(new TestSubcase(m_context, "api-attach-shader", TestSubcase::Create<NegativeAttachShader>)); |
| } |
| } // glcts namespace |