/*------------------------------------------------------------------------- * 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 "gl4cShaderImageLoadStoreTests.hpp" #include "gluContextInfo.hpp" #include "glwEnums.hpp" #include "tcuMatrix.hpp" #include "tcuPlatform.hpp" #include "tcuRenderTarget.hpp" #include "tcuVectorUtil.hpp" #include #include #include #include #include #include #include #include #include namespace gl4cts { using namespace glw; namespace { typedef tcu::Vec2 vec2; typedef tcu::Vec4 vec4; typedef tcu::IVec4 ivec4; typedef tcu::UVec4 uvec4; typedef tcu::Mat4 mat4; class ShaderImageLoadStoreBase : public deqp::SubcaseBase { virtual std::string Title() { return ""; } virtual std::string Purpose() { return ""; } virtual std::string Method() { return ""; } virtual std::string PassCriteria() { return ""; } public: bool SupportedInVS(int requiredVS) { GLint imagesVS; glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &imagesVS); if (imagesVS >= requiredVS) return true; else { std::ostringstream reason; reason << "Required " << requiredVS << " VS image uniforms but only " << imagesVS << " available." << std::endl; OutputNotSupported(reason.str()); return false; } } bool SupportedInTCS(int requiredTCS) { GLint imagesTCS; glGetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &imagesTCS); if (imagesTCS >= requiredTCS) return true; else { std::ostringstream reason; reason << "Required " << requiredTCS << " TCS image uniforms but only " << imagesTCS << " available." << std::endl; OutputNotSupported(reason.str()); return false; } } bool SupportedInTES(int requiredTES) { GLint imagesTES; glGetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &imagesTES); if (imagesTES >= requiredTES) return true; else { std::ostringstream reason; reason << "Required " << requiredTES << " TES image uniforms but only " << imagesTES << " available." << std::endl; OutputNotSupported(reason.str()); return false; } } bool SupportedInGS(int requiredGS) { GLint imagesGS; glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &imagesGS); if (imagesGS >= requiredGS) return true; else { std::ostringstream reason; reason << "Required " << requiredGS << " GS image uniforms but only " << imagesGS << " available." << std::endl; OutputNotSupported(reason.str()); return false; } } bool SupportedInGeomStages(int required) { return SupportedInVS(required) && SupportedInTCS(required) && SupportedInTES(required) && SupportedInGS(required); } bool SupportedInStage(int stage, int required) { switch (stage) { case 0: return SupportedInVS(required); case 1: return SupportedInTCS(required); case 2: return SupportedInTES(required); case 3: return SupportedInGS(required); default: return true; } } bool SupportedSamples(int required) { int i; glGetIntegerv(GL_MAX_IMAGE_SAMPLES, &i); if (i >= required) return true; else { std::ostringstream reason; reason << "Required " << required << " image samples but only " << i << " available." << std::endl; OutputNotSupported(reason.str()); return false; } } int getWindowWidth() { const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget(); return renderTarget.getWidth(); } int getWindowHeight() { const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget(); return renderTarget.getHeight(); } void scaleDimensionsToMemory(int& width, int& height, int devLayers, int sysLayers, int devBPP, int sysBPP) { tcu::PlatformMemoryLimits memoryLimits; m_context.getTestContext().getPlatform().getMemoryLimits(memoryLimits); GLsizeiptr sysSpace = memoryLimits.totalSystemMemory; GLsizeiptr devSpace = memoryLimits.totalDeviceLocalMemory; int devInSysLayers = 0; if (devSpace == 0) { devInSysLayers = devLayers; devLayers = 0; } // Check if available memory is enough GLsizeiptr pixelsPerLayer = width * height; GLsizeiptr sysRequired = pixelsPerLayer * ((sysBPP * sysLayers) + (devBPP * devInSysLayers)); GLsizeiptr devRequired = pixelsPerLayer * devBPP * devLayers; if ((sysRequired <= sysSpace) && (devRequired <= devSpace)) { return; } // Scales the width and height such that the overall texture fits into // the available space for both system and device. GLdouble scale = sqrt(sysSpace / GLdouble(sysRequired)); if (devSpace != 0) { GLdouble devScale = sqrt(devSpace / GLdouble(devRequired)); scale = de::min(devScale, scale); } int newWidth = int(width * scale); int newHeight = int(height * scale); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Reducing surface dimensions to fit in memory, from " << width << "x" << height << " to " << newWidth << "x" << newHeight << "." << tcu::TestLog::EndMessage; width = newWidth; height = newHeight; } inline bool ColorEqual(const vec4& c0, const vec4& c1, const vec4& epsilon) { if (fabs(c0[0] - c1[0]) > epsilon[0]) return false; if (fabs(c0[1] - c1[1]) > epsilon[1]) return false; if (fabs(c0[2] - c1[2]) > epsilon[2]) return false; if (fabs(c0[3] - c1[3]) > epsilon[3]) return false; return true; } bool IsEqual(vec4 a, vec4 b) { return (a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]); } bool Equal(const vec4& v0, const vec4& v1, GLenum internalformat) { if (internalformat == GL_RGBA16_SNORM || internalformat == GL_RG16_SNORM || internalformat == GL_R16_SNORM) { return ColorEqual(v0, v1, vec4(0.0001f)); } else if (internalformat == GL_RGBA8_SNORM || internalformat == GL_RG8_SNORM || internalformat == GL_R8_SNORM) { return ColorEqual(v0, v1, vec4(0.01f)); } return (v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2]) && (v0[3] == v1[3]); } bool Equal(const ivec4& a, const ivec4& b, GLenum) { return (a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]); } bool Equal(const uvec4& a, const uvec4& b, GLenum) { return (a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]); } template std::string ToString(T v) { std::ostringstream s; s << "["; for (int i = 0; i < 4; ++i) s << v[i] << (i == 3 ? "" : ","); s << "]"; return s.str(); } bool ValidateReadBuffer(int x, int y, int w, int h, const vec4& expected) { bool status = true; const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget(); const tcu::PixelFormat& pixelFormat = renderTarget.getPixelFormat(); vec4 g_color_eps = vec4( 1.f / static_cast(1 << pixelFormat.redBits), 1.f / static_cast(1 << pixelFormat.greenBits), 1.f / static_cast(1 << pixelFormat.blueBits), 1.f / static_cast(1 << pixelFormat.alphaBits)); std::vector fb(w * h); glReadPixels(x, y, w, h, GL_RGBA, GL_FLOAT, &fb[0]); for (int yy = 0; yy < h; ++yy) { for (int xx = 0; xx < w; ++xx) { const int idx = yy * w + xx; if (!ColorEqual(fb[idx], expected, g_color_eps)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "First bad color: " << ToString(fb[idx]) << tcu::TestLog::EndMessage; status = false; return status; } } } return status; } bool CompileShader(GLuint shader) { glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLsizei length; GLchar log[1024]; glGetShaderInfoLog(shader, sizeof(log), &length, log); if (length > 1) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader Info Log:\n" << log << tcu::TestLog::EndMessage; } return false; } return true; } bool LinkProgram(GLuint program) { glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLsizei length; GLchar log[1024]; glGetProgramInfoLog(program, sizeof(log), &length, log); if (length > 1) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program Info Log:\n" << log << tcu::TestLog::EndMessage; } return false; } return true; } GLuint BuildProgram(const char* src_vs, const char* src_tcs, const char* src_tes, const char* src_gs, const char* src_fs, bool* result = NULL) { const GLuint p = glCreateProgram(); if (src_vs) { GLuint sh = glCreateShader(GL_VERTEX_SHADER); glAttachShader(p, sh); glDeleteShader(sh); glShaderSource(sh, 1, &src_vs, NULL); if (!CompileShader(sh)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << src_vs << tcu::TestLog::EndMessage; if (result) *result = false; return p; } } if (src_tcs) { GLuint sh = glCreateShader(GL_TESS_CONTROL_SHADER); glAttachShader(p, sh); glDeleteShader(sh); glShaderSource(sh, 1, &src_tcs, NULL); if (!CompileShader(sh)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << src_tcs << tcu::TestLog::EndMessage; if (result) *result = false; return p; } } if (src_tes) { GLuint sh = glCreateShader(GL_TESS_EVALUATION_SHADER); glAttachShader(p, sh); glDeleteShader(sh); glShaderSource(sh, 1, &src_tes, NULL); if (!CompileShader(sh)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << src_tes << tcu::TestLog::EndMessage; if (result) *result = false; return p; } } if (src_gs) { GLuint sh = glCreateShader(GL_GEOMETRY_SHADER); glAttachShader(p, sh); glDeleteShader(sh); glShaderSource(sh, 1, &src_gs, NULL); if (!CompileShader(sh)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << src_gs << tcu::TestLog::EndMessage; if (result) *result = false; return p; } } if (src_fs) { GLuint sh = glCreateShader(GL_FRAGMENT_SHADER); glAttachShader(p, sh); glDeleteShader(sh); glShaderSource(sh, 1, &src_fs, NULL); if (!CompileShader(sh)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << src_fs << tcu::TestLog::EndMessage; if (result) *result = false; return p; } } if (!LinkProgram(p)) { if (src_vs) m_context.getTestContext().getLog() << tcu::TestLog::Message << src_vs << tcu::TestLog::EndMessage; if (src_tcs) m_context.getTestContext().getLog() << tcu::TestLog::Message << src_tcs << tcu::TestLog::EndMessage; if (src_tes) m_context.getTestContext().getLog() << tcu::TestLog::Message << src_tes << tcu::TestLog::EndMessage; if (src_gs) m_context.getTestContext().getLog() << tcu::TestLog::Message << src_gs << tcu::TestLog::EndMessage; if (src_fs) m_context.getTestContext().getLog() << tcu::TestLog::Message << src_fs << tcu::TestLog::EndMessage; if (result) *result = false; return p; } return p; } GLuint BuildShaderProgram(GLenum type, const char* src) { const GLuint p = glCreateShaderProgramv(type, 1, &src); GLint status; glGetProgramiv(p, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLchar log[1024]; glGetProgramInfoLog(p, sizeof(log), NULL, log); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program Info Log:\n" << log << "\n" << src << tcu::TestLog::EndMessage; } return p; } void CreateFullViewportQuad(GLuint* vao, GLuint* vbo, GLuint* ebo) { assert(vao && vbo); // interleaved data (vertex, color0 (green), color1 (blue), color2 (red)) const float v[] = { -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, }; glGenBuffers(1, vbo); glBindBuffer(GL_ARRAY_BUFFER, *vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); if (ebo) { std::vector index_data(4); for (int i = 0; i < 4; ++i) { index_data[i] = static_cast(i); } glGenBuffers(1, ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 4, &index_data[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } glGenVertexArrays(1, vao); glBindVertexArray(*vao); glBindBuffer(GL_ARRAY_BUFFER, *vbo); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 11, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 11, reinterpret_cast(sizeof(float) * 2)); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 11, reinterpret_cast(sizeof(float) * 5)); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 11, reinterpret_cast(sizeof(float) * 8)); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); if (ebo) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ebo); } glBindVertexArray(0); } std::string FormatEnumToString(GLenum e) { switch (e) { case GL_RGBA32F: return "rgba32f"; case GL_RGBA16F: return "rgba16f"; case GL_RG32F: return "rg32f"; case GL_RG16F: return "rg16f"; case GL_R11F_G11F_B10F: return "r11f_g11f_b10f"; case GL_R32F: return "r32f"; case GL_R16F: return "r16f"; case GL_RGBA32UI: return "rgba32ui"; case GL_RGBA16UI: return "rgba16ui"; case GL_RGB10_A2UI: return "rgb10_a2ui"; case GL_RGBA8UI: return "rgba8ui"; case GL_RG32UI: return "rg32ui"; case GL_RG16UI: return "rg16ui"; case GL_RG8UI: return "rg8ui"; case GL_R32UI: return "r32ui"; case GL_R16UI: return "r16ui"; case GL_R8UI: return "r8ui"; case GL_RGBA32I: return "rgba32i"; case GL_RGBA16I: return "rgba16i"; case GL_RGBA8I: return "rgba8i"; case GL_RG32I: return "rg32i"; case GL_RG16I: return "rg16i"; case GL_RG8I: return "rg8i"; case GL_R32I: return "r32i"; case GL_R16I: return "r16i"; case GL_R8I: return "r8i"; case GL_RGBA16: return "rgba16"; case GL_RGB10_A2: return "rgb10_a2"; case GL_RGBA8: return "rgba8"; case GL_RG16: return "rg16"; case GL_RG8: return "rg8"; case GL_R16: return "r16"; case GL_R8: return "r8"; case GL_RGBA16_SNORM: return "rgba16_snorm"; case GL_RGBA8_SNORM: return "rgba8_snorm"; case GL_RG16_SNORM: return "rg16_snorm"; case GL_RG8_SNORM: return "rg8_snorm"; case GL_R16_SNORM: return "r16_snorm"; case GL_R8_SNORM: return "r8_snorm"; } assert(0); return ""; } const char* StageName(int stage) { switch (stage) { case 0: return "Vertex Shader"; case 1: return "Tessellation Control Shader"; case 2: return "Tessellation Evaluation Shader"; case 3: return "Geometry Shader"; case 4: return "Compute Shader"; } assert(0); return NULL; } template GLenum Format(); template GLenum Type(); template std::string TypePrefix(); template GLenum ImageType(GLenum target); void ClearBuffer(GLenum buffer, GLint drawbuffer, const vec4& color) { glClearBufferfv(buffer, drawbuffer, &color[0]); } void ClearBuffer(GLenum buffer, GLint drawbuffer, const ivec4& color) { glClearBufferiv(buffer, drawbuffer, &color[0]); } void ClearBuffer(GLenum buffer, GLint drawbuffer, const uvec4& color) { glClearBufferuiv(buffer, drawbuffer, &color[0]); } bool CheckUniform(GLuint program, const std::string& name, const std::map& name_index_map, GLint size, GLenum type) { std::map::const_iterator iter = name_index_map.find(name); assert(iter != name_index_map.end()); GLchar name_gl[32]; GLsizei length_gl; GLint size_gl; GLenum type_gl; glGetActiveUniform(program, iter->second, sizeof(name_gl), &length_gl, &size_gl, &type_gl, name_gl); if (std::string(name_gl) != name) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform name is " << name_gl << " should be " << name << tcu::TestLog::EndMessage; return false; } if (length_gl != static_cast(name.length())) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform length is " << length_gl << " should be " << name << "(" << name_gl << ")" << tcu::TestLog::EndMessage; return false; } if (size_gl != size) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform size is " << size_gl << " should be " << size << "(" << name_gl << ")" << tcu::TestLog::EndMessage; return false; } if (type_gl != type) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Uniform type is " << type_gl << " should be " << type << "(" << name_gl << ")" << tcu::TestLog::EndMessage; return false; } return true; } bool CheckMax(GLenum pname, GLint min_value) { GLboolean b; GLint i; GLfloat f; GLdouble d; GLint64 i64; glGetIntegerv(pname, &i); if (i < min_value) return false; glGetBooleanv(pname, &b); if (b != (i ? GL_TRUE : GL_FALSE)) return false; glGetFloatv(pname, &f); if (static_cast(f) < min_value) return false; glGetDoublev(pname, &d); if (static_cast(d) < min_value) return false; glGetInteger64v(pname, &i64); if (static_cast(i64) < min_value) return false; return true; } bool CheckBinding(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { GLint i; GLboolean b; glGetIntegeri_v(GL_IMAGE_BINDING_NAME, unit, &i); if (static_cast(i) != texture) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_NAME is " << i << " should be " << texture << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_NAME, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_NAME (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } glGetIntegeri_v(GL_IMAGE_BINDING_LEVEL, unit, &i); if (i != level) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LEVEL is " << i << " should be " << level << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_LEVEL, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LEVEL (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } glGetIntegeri_v(GL_IMAGE_BINDING_LAYERED, unit, &i); if (i != layered) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LAYERED is " << i << " should be " << layered << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_LAYERED, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LAYERED (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } glGetIntegeri_v(GL_IMAGE_BINDING_LAYER, unit, &i); if (i != layer) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LAYER is " << i << " should be " << layer << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_LAYER, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_LAYER (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } glGetIntegeri_v(GL_IMAGE_BINDING_ACCESS, unit, &i); if (static_cast(i) != access) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_ACCESS is " << i << " should be " << access << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_ACCESS, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_ACCESS (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } glGetIntegeri_v(GL_IMAGE_BINDING_FORMAT, unit, &i); if (static_cast(i) != format) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_FORMAT is " << i << " should be " << format << tcu::TestLog::EndMessage; return false; } glGetBooleani_v(GL_IMAGE_BINDING_FORMAT, unit, &b); if (b != (i ? GL_TRUE : GL_FALSE)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_BINDING_FORMAT (as boolean) is " << b << " should be " << (i ? GL_TRUE : GL_FALSE) << tcu::TestLog::EndMessage; return false; } return true; } const char* EnumToString(GLenum e) { switch (e) { case GL_TEXTURE_1D: return "GL_TEXTURE_1D"; case GL_TEXTURE_2D: return "GL_TEXTURE_2D"; case GL_TEXTURE_3D: return "GL_TEXTURE_3D"; case GL_TEXTURE_RECTANGLE: return "GL_TEXTURE_RECTANGLE"; case GL_TEXTURE_CUBE_MAP: return "GL_TEXTURE_CUBE_MAP"; case GL_TEXTURE_1D_ARRAY: return "GL_TEXTURE_1D_ARRAY"; case GL_TEXTURE_2D_ARRAY: return "GL_TEXTURE_2D_ARRAY"; case GL_TEXTURE_CUBE_MAP_ARRAY: return "GL_TEXTURE_CUBE_MAP_ARRAY"; default: assert(0); break; } return NULL; } }; template <> GLenum ShaderImageLoadStoreBase::Format() { return GL_RGBA; } template <> GLenum ShaderImageLoadStoreBase::Format() { return GL_RGBA_INTEGER; } template <> GLenum ShaderImageLoadStoreBase::Format() { return GL_RGBA_INTEGER; } template <> GLenum ShaderImageLoadStoreBase::Format() { return GL_RED_INTEGER; } template <> GLenum ShaderImageLoadStoreBase::Format() { return GL_RED_INTEGER; } template <> GLenum ShaderImageLoadStoreBase::Type() { return GL_FLOAT; } template <> GLenum ShaderImageLoadStoreBase::Type() { return GL_INT; } template <> GLenum ShaderImageLoadStoreBase::Type() { return GL_UNSIGNED_INT; } template <> GLenum ShaderImageLoadStoreBase::Type() { return GL_INT; } template <> GLenum ShaderImageLoadStoreBase::Type() { return GL_UNSIGNED_INT; } template <> std::string ShaderImageLoadStoreBase::TypePrefix() { return ""; } template <> std::string ShaderImageLoadStoreBase::TypePrefix() { return "i"; } template <> std::string ShaderImageLoadStoreBase::TypePrefix() { return "u"; } template <> std::string ShaderImageLoadStoreBase::TypePrefix() { return "i"; } template <> std::string ShaderImageLoadStoreBase::TypePrefix() { return "u"; } template <> GLenum ShaderImageLoadStoreBase::ImageType(GLenum target) { switch (target) { case GL_TEXTURE_1D: return GL_IMAGE_1D; case GL_TEXTURE_2D: return GL_IMAGE_2D; case GL_TEXTURE_3D: return GL_IMAGE_3D; case GL_TEXTURE_RECTANGLE: return GL_IMAGE_2D_RECT; case GL_TEXTURE_CUBE_MAP: return GL_IMAGE_CUBE; case GL_TEXTURE_BUFFER: return GL_IMAGE_BUFFER; case GL_TEXTURE_1D_ARRAY: return GL_IMAGE_1D_ARRAY; case GL_TEXTURE_2D_ARRAY: return GL_IMAGE_2D_ARRAY; case GL_TEXTURE_CUBE_MAP_ARRAY: return GL_IMAGE_CUBE_MAP_ARRAY; case GL_TEXTURE_2D_MULTISAMPLE: return GL_IMAGE_2D_MULTISAMPLE; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return GL_IMAGE_2D_MULTISAMPLE_ARRAY; } assert(0); return 0; } template <> GLenum ShaderImageLoadStoreBase::ImageType(GLenum target) { switch (target) { case GL_TEXTURE_1D: return GL_INT_IMAGE_1D; case GL_TEXTURE_2D: return GL_INT_IMAGE_2D; case GL_TEXTURE_3D: return GL_INT_IMAGE_3D; case GL_TEXTURE_RECTANGLE: return GL_INT_IMAGE_2D_RECT; case GL_TEXTURE_CUBE_MAP: return GL_INT_IMAGE_CUBE; case GL_TEXTURE_BUFFER: return GL_INT_IMAGE_BUFFER; case GL_TEXTURE_1D_ARRAY: return GL_INT_IMAGE_1D_ARRAY; case GL_TEXTURE_2D_ARRAY: return GL_INT_IMAGE_2D_ARRAY; case GL_TEXTURE_CUBE_MAP_ARRAY: return GL_INT_IMAGE_CUBE_MAP_ARRAY; case GL_TEXTURE_2D_MULTISAMPLE: return GL_INT_IMAGE_2D_MULTISAMPLE; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY; } assert(0); return 0; } template <> GLenum ShaderImageLoadStoreBase::ImageType(GLenum target) { switch (target) { case GL_TEXTURE_1D: return GL_UNSIGNED_INT_IMAGE_1D; case GL_TEXTURE_2D: return GL_UNSIGNED_INT_IMAGE_2D; case GL_TEXTURE_3D: return GL_UNSIGNED_INT_IMAGE_3D; case GL_TEXTURE_RECTANGLE: return GL_UNSIGNED_INT_IMAGE_2D_RECT; case GL_TEXTURE_CUBE_MAP: return GL_UNSIGNED_INT_IMAGE_CUBE; case GL_TEXTURE_BUFFER: return GL_UNSIGNED_INT_IMAGE_BUFFER; case GL_TEXTURE_1D_ARRAY: return GL_UNSIGNED_INT_IMAGE_1D_ARRAY; case GL_TEXTURE_2D_ARRAY: return GL_UNSIGNED_INT_IMAGE_2D_ARRAY; case GL_TEXTURE_CUBE_MAP_ARRAY: return GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY; case GL_TEXTURE_2D_MULTISAMPLE: return GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY; } assert(0); return 0; } //----------------------------------------------------------------------------- // 1.1.1 BasicAPIGet //----------------------------------------------------------------------------- class BasicAPIGet : public ShaderImageLoadStoreBase { virtual long Run() { if (!CheckMax(GL_MAX_IMAGE_UNITS, 8)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_IMAGE_UNITS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, 8)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_IMAGE_SAMPLES, 0)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_IMAGE_SAMPLES value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_VERTEX_IMAGE_UNIFORMS, 0)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_VERTEX_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 0)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, 0)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, 0)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_GEOMETRY_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, 8)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_FRAGMENT_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } if (!CheckMax(GL_MAX_COMBINED_IMAGE_UNIFORMS, 8)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_COMBINED_IMAGE_UNIFORMS value is invalid." << tcu::TestLog::EndMessage; return ERROR; } return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.1.2 BasicAPIBind //----------------------------------------------------------------------------- class BasicAPIBind : public ShaderImageLoadStoreBase { GLuint m_texture; virtual long Setup() { m_texture = 0; return NO_ERROR; } virtual long Run() { for (GLuint index = 0; index < 8; ++index) { if (!CheckBinding(index, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Binding point " << index << " has invalid default state." << tcu::TestLog::EndMessage; return ERROR; } } glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R32F, 16, 16, 4, 0, GL_RED, GL_FLOAT, NULL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_R32F, 8, 8, 4, 0, GL_RED, GL_FLOAT, NULL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 2, GL_R32F, 4, 4, 4, 0, GL_RED, GL_FLOAT, NULL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 3, GL_R32F, 2, 2, 4, 0, GL_RED, GL_FLOAT, NULL); glTexImage3D(GL_TEXTURE_2D_ARRAY, 4, GL_R32F, 1, 1, 4, 0, GL_RED, GL_FLOAT, NULL); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32F); if (!CheckBinding(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32F)) return ERROR; glBindImageTexture(1, m_texture, 1, GL_TRUE, 1, GL_WRITE_ONLY, GL_RGBA8); if (!CheckBinding(1, m_texture, 1, GL_TRUE, 1, GL_WRITE_ONLY, GL_RGBA8)) return ERROR; glBindImageTexture(4, m_texture, 3, GL_FALSE, 2, GL_READ_ONLY, GL_RG16); if (!CheckBinding(4, m_texture, 3, GL_FALSE, 2, GL_READ_ONLY, GL_RG16)) return ERROR; glBindImageTexture(7, m_texture, 4, GL_FALSE, 3, GL_READ_ONLY, GL_R32I); if (!CheckBinding(7, m_texture, 4, GL_FALSE, 3, GL_READ_ONLY, GL_R32I)) return ERROR; glDeleteTextures(1, &m_texture); m_texture = 0; for (GLuint index = 0; index < 8; ++index) { GLint name; glGetIntegeri_v(GL_IMAGE_BINDING_NAME, index, &name); if (name != 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Binding point " << index << " should be set to 0 after texture deletion." << tcu::TestLog::EndMessage; return ERROR; } } return NO_ERROR; } virtual long Cleanup() { glDeleteTextures(1, &m_texture); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.1.3 BasicAPIBarrier //----------------------------------------------------------------------------- class BasicAPIBarrier : public ShaderImageLoadStoreBase { virtual long Run() { glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT); glMemoryBarrier(GL_ELEMENT_ARRAY_BARRIER_BIT); glMemoryBarrier(GL_UNIFORM_BARRIER_BIT); glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glMemoryBarrier(GL_COMMAND_BARRIER_BIT); glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); glMemoryBarrier(GL_TRANSFORM_FEEDBACK_BARRIER_BIT); glMemoryBarrier(GL_ATOMIC_COUNTER_BARRIER_BIT); glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_COMMAND_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT | GL_TRANSFORM_FEEDBACK_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT); glMemoryBarrier(GL_ALL_BARRIER_BITS); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.1.4 BasicAPITexParam //----------------------------------------------------------------------------- class BasicAPITexParam : public ShaderImageLoadStoreBase { GLuint m_texture; virtual long Setup() { m_texture = 0; return NO_ERROR; } virtual long Run() { glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, 16, 16, 0, GL_RED, GL_FLOAT, NULL); GLint i; GLfloat f; GLuint ui; glGetTexParameteriv(GL_TEXTURE_2D, GL_IMAGE_FORMAT_COMPATIBILITY_TYPE, &i); if (i != GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE should equal to " << "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE for textures allocated by the GL." << tcu::TestLog::EndMessage; return ERROR; } glGetTexParameterfv(GL_TEXTURE_2D, GL_IMAGE_FORMAT_COMPATIBILITY_TYPE, &f); if (static_cast(f) != GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE should equal to " << "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE for textures allocated by the GL." << tcu::TestLog::EndMessage; return ERROR; } glGetTexParameterIiv(GL_TEXTURE_2D, GL_IMAGE_FORMAT_COMPATIBILITY_TYPE, &i); if (i != GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE should equal to " << "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE for textures allocated by the GL." << tcu::TestLog::EndMessage; return ERROR; } glGetTexParameterIuiv(GL_TEXTURE_2D, GL_IMAGE_FORMAT_COMPATIBILITY_TYPE, &ui); if (ui != GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE should equal to " << "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE for textures allocated by the GL." << tcu::TestLog::EndMessage; return ERROR; } return NO_ERROR; } virtual long Cleanup() { glDeleteTextures(1, &m_texture); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.2.1 BasicAllFormatsStore //----------------------------------------------------------------------------- class BasicAllFormatsStore : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!Write(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; return NO_ERROR; } template bool Write(GLenum internalformat, const T& write_value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFS(internalformat, write_value).c_str()); const int kSize = 16; std::vector data(kSize * kSize); GLuint texture; glGenTextures(1, &texture); for (GLuint unit = 0; unit < 8; ++unit) { glBindTexture(GL_TEXTURE_2D, texture); 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, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glViewport(0, 0, kSize, kSize); glUseProgram(program); glBindImageTexture(unit, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindTexture(GL_TEXTURE_2D, texture); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, Format(), Type(), &data[0]); for (int i = 0; i < kSize * kSize; ++i) { if (!Equal(data[i], expected_value, internalformat)) { glDeleteTextures(1, &texture); glUseProgram(0); glDeleteProgram(program); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << ". Format is: " << FormatEnumToString(internalformat) << ". Unit is: " << unit << tcu::TestLog::EndMessage; return false; } } if (unit < 7) { glUniform1i(glGetUniformLocation(program, "g_image"), static_cast(unit + 1)); } } glDeleteTextures(1, &texture); glUseProgram(0); glDeleteProgram(program); return true; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template std::string GenFS(GLenum internalformat, const T& value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image, coord, " << TypePrefix() << "vec4" << value << ");" NL " discard;" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.2.2 BasicAllFormatsLoad //----------------------------------------------------------------------------- class BasicAllFormatsLoad : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!Read(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; return NO_ERROR; } template bool Read(GLenum internalformat, const T& value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFS(internalformat, expected_value).c_str()); const int kSize = 16; std::vector data(kSize * kSize, value); GLuint texture; glGenTextures(1, &texture); for (GLuint unit = 0; unit < 8; ++unit) { glBindTexture(GL_TEXTURE_2D, texture); 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, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glViewport(0, 0, kSize, kSize); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glBindImageTexture(unit, texture, 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { glDeleteTextures(1, &texture); glUseProgram(0); glDeleteProgram(program); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Bad load value. Format is: " << FormatEnumToString(internalformat) << ". Unit is: " << unit << tcu::TestLog::EndMessage; return false; } if (unit < 7) { glUniform1i(glGetUniformLocation(program, "g_image"), static_cast(unit + 1)); } } glDeleteTextures(1, &texture); glUseProgram(0); glDeleteProgram(program); return true; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template std::string GenFS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " else o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.2.3 BasicAllFormatsStoreGeometryStages //----------------------------------------------------------------------------- class BasicAllFormatsStoreGeometryStages : public ShaderImageLoadStoreBase { GLuint m_vao; virtual long Setup() { glGenVertexArrays(1, &m_vao); return NO_ERROR; } virtual long Run() { if (!SupportedInGeomStages(1)) return NOT_SUPPORTED; glEnable(GL_RASTERIZER_DISCARD); if (!Write(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; return NO_ERROR; } template bool Write(GLenum internalformat, const T& write_value, const T& expected_value) { const GLuint program = BuildProgram(GenVS(internalformat, write_value).c_str(), GenTCS(internalformat, write_value).c_str(), GenTES(internalformat, write_value).c_str(), GenGS(internalformat, write_value).c_str(), NULL); const int kSize = 1; std::vector data(kSize * kSize); GLuint texture[4]; glGenTextures(4, texture); for (int i = 0; i < 4; ++i) { glBindTexture(GL_TEXTURE_2D_ARRAY, texture[i]); 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, internalformat, kSize, kSize, 1, 0, Format(), Type(), &data[0]); } glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image0"), 0); glUniform1i(glGetUniformLocation(program, "g_image1"), 1); glUniform1i(glGetUniformLocation(program, "g_image2"), 2); glUniform1i(glGetUniformLocation(program, "g_image3"), 3); for (GLuint i = 0; i < 4; ++i) { glBindImageTexture(i, texture[i], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); } glBindVertexArray(m_vao); glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, 1); glPatchParameteri(GL_PATCH_VERTICES, 3); for (int i = 0; i < 4; ++i) { glBindTexture(GL_TEXTURE_2D_ARRAY, texture[i]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, Format(), Type(), &data[0]); if (!Equal(data[0], expected_value, internalformat)) { glDeleteTextures(4, texture); glUseProgram(0); glDeleteProgram(program); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value is: " << ToString(data[0]) << ". Value should be: " << ToString(expected_value) << ". Format is: " << FormatEnumToString(internalformat) << ". Stage is: " << StageName(i) << tcu::TestLog::EndMessage; return false; } } glDeleteTextures(4, texture); glUseProgram(0); glDeleteProgram(program); return true; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } template std::string GenVS(GLenum internalformat, const T& value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image0;" NL "void main() {" NL " ivec3 coord = ivec3(gl_VertexID, 0, 0);" NL " imageStore(g_image0, coord, " << TypePrefix() << "vec4" << value << ");" NL "}"; return os.str(); } template std::string GenTCS(GLenum internalformat, const T& value) { std::ostringstream os; os << "#version 420 core" NL "layout(vertices = 1) out;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image1;" NL "void main() {" NL " gl_TessLevelInner[0] = 1;" NL " gl_TessLevelInner[1] = 1;" NL " gl_TessLevelOuter[0] = 1;" NL " gl_TessLevelOuter[1] = 1;" NL " gl_TessLevelOuter[2] = 1;" NL " gl_TessLevelOuter[3] = 1;" NL " ivec3 coord = ivec3(gl_PrimitiveID, 0, 0);" NL " imageStore(g_image1, coord, " << TypePrefix() << "vec4" << value << ");" NL "}"; return os.str(); } template std::string GenTES(GLenum internalformat, const T& value) { std::ostringstream os; os << "#version 420 core" NL "layout(triangles, point_mode) in;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image2;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveID, 0, 0);" NL " imageStore(g_image2, coord, " << TypePrefix() << "vec4" << value << ");" NL "}"; return os.str(); } template std::string GenGS(GLenum internalformat, const T& value) { std::ostringstream os; os << "#version 420 core" NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image3;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveIDIn, 0, 0);" NL " imageStore(g_image3, coord, " << TypePrefix() << "vec4" << value << ");" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.2.4 BasicAllFormatsLoadGeometryStages //----------------------------------------------------------------------------- class BasicAllFormatsLoadGeometryStages : public ShaderImageLoadStoreBase { GLuint m_vao; virtual long Setup() { glGenVertexArrays(1, &m_vao); return NO_ERROR; } virtual long Run() { if (!SupportedInGeomStages(2)) return NOT_SUPPORTED; glEnable(GL_RASTERIZER_DISCARD); if (!Read(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; return NO_ERROR; } template bool Read(GLenum internalformat, const T& value, const T& expected_value) { const GLuint program = BuildProgram( GenVS(internalformat, expected_value).c_str(), GenTCS(internalformat, expected_value).c_str(), GenTES(internalformat, expected_value).c_str(), GenGS(internalformat, expected_value).c_str(), NULL); const int kSize = 1; std::vector data(kSize * kSize, value); GLuint texture[8]; glGenTextures(8, texture); for (int i = 0; i < 4; ++i) { glBindTexture(GL_TEXTURE_2D_ARRAY, texture[i]); 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, internalformat, kSize, kSize, 1, 0, Format(), Type(), &data[0]); } glBindTexture(GL_TEXTURE_2D_ARRAY, 0); vec4 zero(0); for (int i = 4; i < 8; ++i) { glBindTexture(GL_TEXTURE_2D, texture[i]); 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, kSize, kSize, 0, GL_RGBA, GL_FLOAT, &zero); } glBindTexture(GL_TEXTURE_2D, 0); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image0"), 0); glUniform1i(glGetUniformLocation(program, "g_image1"), 1); glUniform1i(glGetUniformLocation(program, "g_image2"), 2); glUniform1i(glGetUniformLocation(program, "g_image3"), 3); glUniform1i(glGetUniformLocation(program, "g_image0_result"), 4); glUniform1i(glGetUniformLocation(program, "g_image1_result"), 5); glUniform1i(glGetUniformLocation(program, "g_image2_result"), 6); glUniform1i(glGetUniformLocation(program, "g_image3_result"), 7); for (GLuint i = 0; i < 4; ++i) { glBindImageTexture(i, texture[i], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); } for (GLuint i = 4; i < 8; ++i) { glBindImageTexture(i, texture[i], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); } glBindVertexArray(m_vao); glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, 1); glPatchParameteri(GL_PATCH_VERTICES, 3); const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget(); const tcu::PixelFormat& pixelFormat = renderTarget.getPixelFormat(); vec4 g_color_eps = vec4( 1.f / static_cast(1 << pixelFormat.redBits), 1.f / static_cast(1 << pixelFormat.greenBits), 1.f / static_cast(1 << pixelFormat.blueBits), 1.f / static_cast(1 << pixelFormat.alphaBits)); for (int i = 0; i < 4; ++i) { glBindTexture(GL_TEXTURE_2D, texture[i + 4]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); vec4 result; glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &result[0]); if (!ColorEqual(result, vec4(0, 1, 0, 1), g_color_eps)) { glDeleteTextures(8, texture); glUseProgram(0); glDeleteProgram(program); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Bad load value. Format is: " << FormatEnumToString(internalformat) << ". Stage is: " << StageName(i) << tcu::TestLog::EndMessage; return false; } } glDeleteTextures(8, texture); glUseProgram(0); glDeleteProgram(program); return true; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } template std::string GenVS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image0;" NL "layout(rgba32f) writeonly uniform image2D g_image0_result;" NL "void main() {" NL " ivec3 coord = ivec3(gl_VertexID, 0, 0);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image0, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") imageStore(g_image0_result, coord.xy, vec4(1.0, 0.0, 0.0, 1.0));" NL " else imageStore(g_image0_result, coord.xy, vec4(0.0, 1.0, 0.0, 1.0));" NL "}"; return os.str(); } template std::string GenTCS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(vertices = 1) out;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image1;" NL "layout(rgba32f) writeonly uniform image2D g_image1_result;" NL "void main() {" NL " gl_TessLevelInner[0] = 1;" NL " gl_TessLevelInner[1] = 1;" NL " gl_TessLevelOuter[0] = 1;" NL " gl_TessLevelOuter[1] = 1;" NL " gl_TessLevelOuter[2] = 1;" NL " gl_TessLevelOuter[3] = 1;" NL " ivec3 coord = ivec3(gl_PrimitiveID, 0, 0);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image1, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") imageStore(g_image1_result, coord.xy, vec4(1.0, 0.0, 0.0, 1.0));" NL " else imageStore(g_image1_result, coord.xy, vec4(0.0, 1.0, 0.0, 1.0));" NL "}"; return os.str(); } template std::string GenTES(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(triangles, point_mode) in;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image2;" NL "layout(rgba32f) writeonly uniform image2D g_image2_result;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveID, 0, 0);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image2, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") imageStore(g_image2_result, coord.xy, vec4(1.0, 0.0, 0.0, 1.0));" NL " else imageStore(g_image2_result, coord.xy, vec4(0.0, 1.0, 0.0, 1.0));" NL "}"; return os.str(); } template std::string GenGS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image3;" NL "layout(rgba32f) writeonly uniform image2D g_image3_result;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveIDIn, 0, 0);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image3, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") imageStore(g_image3_result, coord.xy, vec4(1.0, 0.0, 0.0, 1.0));" NL " else imageStore(g_image3_result, coord.xy, vec4(0.0, 1.0, 0.0, 1.0));" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.2.5 BasicAllFormatsLoadStoreComputeStage //----------------------------------------------------------------------------- class BasicAllFormatsLoadStoreComputeStage : public ShaderImageLoadStoreBase { virtual long Run() { if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader")) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_ARB_compute_shader not supported, skipping test" << tcu::TestLog::EndMessage; return NOT_SUPPORTED; } if (!Read(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Read(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Read(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Read(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Read(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Read(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Read(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Read(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Read(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Read(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; return NO_ERROR; } template bool Read(GLenum internalformat, const T& value, const T& expected_value) { GLuint program; std::string source = GenCS(internalformat); const char* const src = source.c_str(); GLuint sh = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(sh, 1, &src, NULL); glCompileShader(sh); program = glCreateProgram(); glAttachShader(program, sh); glLinkProgram(program); glDeleteShader(sh); const int kSize = 1; std::vector data(kSize * kSize, value); GLuint texture[2]; glGenTextures(2, texture); glBindTexture(GL_TEXTURE_2D, 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, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); vec4 zero(0); glTexImage2D(GL_TEXTURE_2D, 0, internalformat, kSize, kSize, 0, Format(), Type(), &zero); glBindTexture(GL_TEXTURE_2D, 0); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_read"), 0); glUniform1i(glGetUniformLocation(program, "g_image_write"), 1); glBindImageTexture(0, texture[0], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(1, texture[1], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glDispatchCompute(1, 1, 1); for (int i = 0; i < 2; ++i) { glBindTexture(GL_TEXTURE_2D, texture[i]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, Format(), Type(), &data[0]); if (!Equal(data[0], expected_value, internalformat)) { glDeleteTextures(4, texture); glUseProgram(0); glDeleteProgram(program); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value is: " << ToString(data[0]) << ". Value should be: " << ToString(expected_value) << ". Format is: " << FormatEnumToString(internalformat) << tcu::TestLog::EndMessage; return false; } } glDeleteTextures(2, texture); glUseProgram(0); glDeleteProgram(program); return true; } template std::string GenCS(GLenum internalformat) { std::ostringstream os; os << "#version 420 core" NL "#extension GL_ARB_compute_shader : require" NL "layout(local_size_x = 1) in;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2D g_image_read;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2D g_image_write;" NL "void main() {" NL " ivec2 coord = ivec2(int(gl_GlobalInvocationID.x), 0);" NL " " << TypePrefix() << "vec4 v = imageLoad(g_image_read, coord);" NL " imageStore(g_image_write, coord, v);" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.3.1 BasicAllTargetsStore //----------------------------------------------------------------------------- class BasicAllTargetsStore : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!Write(GL_RGBA32F, vec4(-1.0f, 2.0f, 3.0f, -4.0f), vec4(-1.0f, 2.0f, 3.0f, -4.0f))) return ERROR; if (!Write(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RGBA32UI, uvec4(1, 2, 3, 4), uvec4(1, 2, 3, 4))) return ERROR; if (!WriteCubeArray(GL_RGBA32F, vec4(-1.0f, 2.0f, 3.0f, -4.0f), vec4(-1.0f, 2.0f, 3.0f, -4.0f))) return ERROR; if (!WriteCubeArray(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!WriteCubeArray(GL_RGBA32UI, uvec4(1, 2, 3, 4), uvec4(1, 2, 3, 4))) return ERROR; if (SupportedSamples(4)) { if (!WriteMS(GL_RGBA32F, vec4(-1.0f, 2.0f, 3.0f, -4.0f), vec4(-1.0f, 2.0f, 3.0f, -4.0f))) return ERROR; GLint isamples; glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &isamples); if (isamples >= 4) { if (!WriteMS(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!WriteMS(GL_RGBA32UI, uvec4(1, 2, 3, 4), uvec4(1, 2, 3, 4))) return ERROR; } } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template bool Write(GLenum internalformat, const T& write_value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFS(internalformat, write_value).c_str()); GLuint textures[8]; GLuint buffer; glGenTextures(8, textures); glGenBuffers(1, &buffer); const int kSize = 16; std::vector data(kSize * kSize * 2); glBindTexture(GL_TEXTURE_1D, textures[0]); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage1D(GL_TEXTURE_1D, 0, internalformat, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D, 0); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_3D, textures[2]); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage3D(GL_TEXTURE_3D, 0, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_3D, 0); glBindTexture(GL_TEXTURE_RECTANGLE, textures[3]); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_RECTANGLE, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, textures[4]); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindBuffer(GL_TEXTURE_BUFFER, buffer); glBufferData(GL_TEXTURE_BUFFER, kSize * sizeof(T), &data[0], GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_BUFFER, textures[5]); glTexBuffer(GL_TEXTURE_BUFFER, internalformat, buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_1D_ARRAY, textures[6]); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, internalformat, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D_ARRAY, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, textures[7]); 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, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(1, textures[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(2, textures[2], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(3, textures[3], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(4, textures[4], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(5, textures[5], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(6, textures[6], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(7, textures[7], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_1d"), 0); glUniform1i(glGetUniformLocation(program, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program, "g_image_cube"), 4); glUniform1i(glGetUniformLocation(program, "g_image_buffer"), 5); glUniform1i(glGetUniformLocation(program, "g_image_1darray"), 6); glUniform1i(glGetUniformLocation(program, "g_image_2darray"), 7); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; glBindTexture(GL_TEXTURE_1D, textures[0]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_1D, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D, 0); for (int i = 0; i < kSize; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_1D target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } std::fill(data.begin(), data.end(), T(0)); glBindTexture(GL_TEXTURE_2D, textures[1]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); for (int i = 0; i < kSize * kSize; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_2D target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glBindTexture(GL_TEXTURE_3D, textures[2]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_3D, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_3D, 0); for (int i = 0; i < kSize * kSize * 2; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_3D target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glBindTexture(GL_TEXTURE_RECTANGLE, textures[3]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_RECTANGLE, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_RECTANGLE, 0); for (int i = 0; i < kSize * kSize; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_RECTANGLE target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } { glBindTexture(GL_TEXTURE_CUBE_MAP, textures[4]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); for (int face = 0; face < 6; ++face) { glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, Format(), Type(), &data[0]); for (int i = 0; i < kSize * kSize; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_CUBE_MAP_POSITIVE_X target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } } glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } glBindTexture(GL_TEXTURE_BUFFER, textures[5]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glBindTexture(GL_TEXTURE_BUFFER, 0); glBindBuffer(GL_TEXTURE_BUFFER, buffer); glGetBufferSubData(GL_TEXTURE_BUFFER, 0, kSize * sizeof(T), &data[0]); glBindBuffer(GL_TEXTURE_BUFFER, 0); for (int i = 0; i < kSize; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_BUFFER target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glBindTexture(GL_TEXTURE_1D_ARRAY, textures[6]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_1D_ARRAY, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D_ARRAY, 0); for (int i = 0; i < kSize * 2; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_1D_ARRAY target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glBindTexture(GL_TEXTURE_2D_ARRAY, textures[7]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); for (int i = 0; i < kSize * kSize * 2; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_2D_ARRAY target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glUseProgram(0); glDeleteProgram(program); glDeleteTextures(8, textures); glDeleteBuffers(1, &buffer); return status; } template bool WriteMS(GLenum internalformat, const T& write_value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSMS(internalformat, write_value).c_str()); const GLuint val_program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSMSVal(expected_value).c_str()); GLuint textures[2]; glGenTextures(2, textures); const int kSize = 16; glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures[0]); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, internalformat, kSize, kSize, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures[1]); glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, internalformat, kSize, kSize, 2, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); glBindImageTexture(1, textures[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(4, textures[1], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_2dms"), 1); glUniform1i(glGetUniformLocation(program, "g_image_2dms_array"), 4); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures[0]); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures[1]); glUseProgram(val_program); glUniform1i(glGetUniformLocation(val_program, "g_sampler_2dms"), 0); glUniform1i(glGetUniformLocation(val_program, "g_sampler_2dms_array"), 1); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_MULTISAMPLE_ARRAY target failed." << tcu::TestLog::EndMessage; } glActiveTexture(GL_TEXTURE0); glDeleteTextures(2, textures); glUseProgram(0); glDeleteProgram(program); glDeleteProgram(val_program); return status; } template bool WriteCubeArray(GLenum internalformat, const T& write_value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSCubeArray(internalformat, write_value).c_str()); GLuint textures[1]; glGenTextures(1, textures); const int kSize = 16; std::vector data(kSize * kSize * 12); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures[0]); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, kSize, kSize, 12, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glUseProgram(program); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; std::fill(data.begin(), data.end(), T(0)); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures[0]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_CUBE_MAP_ARRAY, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); for (int i = 0; i < kSize * kSize * 12; ++i) { if (!tcu::allEqual(data[i], expected_value)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_TEXTURE_CUBE_MAP_ARRAY target failed. Value is: " << ToString(data[i]) << ". Value should be: " << ToString(expected_value) << tcu::TestLog::EndMessage; break; } } glDeleteTextures(1, textures); glUseProgram(0); glDeleteProgram(program); return status; } template std::string GenFS(GLenum internalformat, const T& write_value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image_1d, coord.x, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2d, coord, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_3d, ivec3(coord.xy, 0), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_3d, ivec3(coord.xy, 1), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2drect, coord, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 0), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 1), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 2), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 3), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 4), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 5), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_buffer, coord.x, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_1darray, ivec2(coord.x, 0), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_1darray, ivec2(coord.x, 1), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2darray, ivec3(coord, 0), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2darray, ivec3(coord, 1), " << TypePrefix() << "vec4" << write_value << ");" NL " discard;" NL "}"; return os.str(); } template std::string GenFSMS(GLenum internalformat, const T& write_value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DMSArray g_image_2dms_array;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image_2dms, coord, 0, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 1, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 2, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 3, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 0, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 1, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 2, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 3, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 0, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 1, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 2, " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 3, " << TypePrefix() << "vec4" << write_value << ");" NL " discard;" NL "}"; return os.str(); } template std::string GenFSMSVal(const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "uniform " << TypePrefix() << "sampler2DMS g_sampler_2dms;" NL "uniform " << TypePrefix() << "sampler2DMSArray g_sampler_2dms_array;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " if (texelFetch(g_sampler_2dms, coord, 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 0), 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 0), 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 0), 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 0), 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 1), 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 1), 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 1), 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (texelFetch(g_sampler_2dms_array, ivec3(coord, 1), 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL "}"; return os.str(); } template std::string GenFSCubeArray(GLenum internalformat, const T& write_value) { std::ostringstream os; os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "imageCubeArray g_image_cube_array;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image_cube_array, ivec3(coord, 0), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 1), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 2), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 3), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 4), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 5), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 6), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 7), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 8), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 9), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 10), " << TypePrefix() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 11), " << TypePrefix() << "vec4" << write_value << ");" NL " discard;" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.3.2.1 BasicAllTargetsLoadNonMS //----------------------------------------------------------------------------- class BasicAllTargetsLoadNonMS : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!Read(GL_RGBA32F, vec4(-1.0f, 10.0f, -200.0f, 3000.0f), vec4(-1.0f, 10.0f, -200.0f, 3000.0f))) return ERROR; if (!Read(GL_RGBA32I, ivec4(-1, 10, -200, 3000), ivec4(-1, 10, -200, 3000))) return ERROR; if (!Read(GL_RGBA32UI, uvec4(1, 10, 200, 3000), uvec4(1, 10, 200, 3000))) return ERROR; if (!ReadCube(GL_RGBA32F, vec4(-1.0f, 10.0f, -200.0f, 3000.0f), vec4(-1.0f, 10.0f, -200.0f, 3000.0f))) return ERROR; if (!ReadCube(GL_RGBA32I, ivec4(-1, 10, -200, 3000), ivec4(-1, 10, -200, 3000))) return ERROR; if (!ReadCube(GL_RGBA32UI, uvec4(1, 10, 200, 3000), uvec4(1, 10, 200, 3000))) return ERROR; return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template bool Read(GLenum internalformat, const T& value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFS(internalformat, expected_value).c_str()); GLuint textures[7]; GLuint buffer; glGenTextures(7, textures); glGenBuffers(1, &buffer); const int kSize = 16; std::vector data(kSize * kSize * 2, value); glBindTexture(GL_TEXTURE_1D, textures[0]); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage1D(GL_TEXTURE_1D, 0, internalformat, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D, 0); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_3D, textures[2]); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage3D(GL_TEXTURE_3D, 0, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_3D, 0); glBindTexture(GL_TEXTURE_RECTANGLE, textures[3]); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_RECTANGLE, 0); glBindBuffer(GL_TEXTURE_BUFFER, buffer); glBufferData(GL_TEXTURE_BUFFER, kSize * sizeof(T), &data[0], GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_BUFFER, textures[4]); glTexBuffer(GL_TEXTURE_BUFFER, internalformat, buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_1D_ARRAY, textures[5]); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, internalformat, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D_ARRAY, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, textures[6]); 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, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(1, textures[1], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(2, textures[2], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(3, textures[3], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(4, textures[4], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(5, textures[5], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(6, textures[6], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_1d"), 0); glUniform1i(glGetUniformLocation(program, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program, "g_image_buffer"), 4); glUniform1i(glGetUniformLocation(program, "g_image_1darray"), 5); glUniform1i(glGetUniformLocation(program, "g_image_2darray"), 6); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; } std::map name_index_map; GLint uniforms; glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniforms); if (uniforms != 7) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "ACTIVE_UNIFORMS is " << uniforms << " should be 7." << tcu::TestLog::EndMessage; } for (GLuint index = 0; index < static_cast(uniforms); ++index) { GLchar name[32]; glGetActiveUniformName(program, index, sizeof(name), NULL, name); name_index_map.insert(std::make_pair(std::string(name), index)); } if (!CheckUniform(program, "g_image_1d", name_index_map, 1, ImageType(GL_TEXTURE_1D))) status = false; if (!CheckUniform(program, "g_image_2d", name_index_map, 1, ImageType(GL_TEXTURE_2D))) status = false; if (!CheckUniform(program, "g_image_3d", name_index_map, 1, ImageType(GL_TEXTURE_3D))) status = false; if (!CheckUniform(program, "g_image_2drect", name_index_map, 1, ImageType(GL_TEXTURE_RECTANGLE))) status = false; if (!CheckUniform(program, "g_image_buffer", name_index_map, 1, ImageType(GL_TEXTURE_BUFFER))) status = false; if (!CheckUniform(program, "g_image_1darray", name_index_map, 1, ImageType(GL_TEXTURE_1D_ARRAY))) status = false; if (!CheckUniform(program, "g_image_2darray", name_index_map, 1, ImageType(GL_TEXTURE_2D_ARRAY))) status = false; glUseProgram(0); glDeleteProgram(program); glDeleteTextures(7, textures); glDeleteBuffers(1, &buffer); return status; } template bool ReadCube(GLenum internalformat, const T& value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSCube(internalformat, expected_value).c_str()); GLuint textures[2]; glGenTextures(2, textures); const int kSize = 16; std::vector data(kSize * kSize * 12, value); glBindTexture(GL_TEXTURE_CUBE_MAP, textures[0]); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures[1]); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, kSize, kSize, 12, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(1, textures[1], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_cube"), 0); glUniform1i(glGetUniformLocation(program, "g_image_cube_array"), 1); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; } std::map name_index_map; GLint uniforms; glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniforms); if (uniforms != 2) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "ACTIVE_UNIFORMS is " << uniforms << " should be 2." << tcu::TestLog::EndMessage; } for (GLuint index = 0; index < static_cast(uniforms); ++index) { GLchar name[32]; glGetActiveUniformName(program, index, sizeof(name), NULL, name); name_index_map.insert(std::make_pair(std::string(name), index)); } if (!CheckUniform(program, "g_image_cube", name_index_map, 1, ImageType(GL_TEXTURE_CUBE_MAP))) status = false; if (!CheckUniform(program, "g_image_cube_array", name_index_map, 1, ImageType(GL_TEXTURE_CUBE_MAP_ARRAY))) status = false; glUseProgram(0); glDeleteProgram(program); glDeleteTextures(2, textures); return status; } template std::string GenFS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " " << TypePrefix() << "vec4 v;" NL " v = imageLoad(g_image_1d, coord.x);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.05);" NL " v = imageLoad(g_image_2d, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " v = imageLoad(g_image_3d, ivec3(coord.xy, 0));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.15);" NL " v = imageLoad(g_image_3d, ivec3(coord.xy, 1));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " v = imageLoad(g_image_2drect, coord);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.25);" NL " v = imageLoad(g_image_buffer, coord.x);" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.6);" NL " v = imageLoad(g_image_1darray, ivec2(coord.x, 0));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.65);" NL " v = imageLoad(g_image_1darray, ivec2(coord.x, 1));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.7);" NL " v = imageLoad(g_image_2darray, ivec3(coord, 0));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.75);" NL " v = imageLoad(g_image_2darray, ivec3(coord, 1));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.8);" NL "}"; return os.str(); } template std::string GenFSCube(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "imageCubeArray g_image_cube_array;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " " << TypePrefix() << "vec4 v;" NL " v = imageLoad(g_image_cube, ivec3(coord, 0));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " v = imageLoad(g_image_cube, ivec3(coord, 1));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.35);" NL " v = imageLoad(g_image_cube, ivec3(coord, 2));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.4);" NL " v = imageLoad(g_image_cube, ivec3(coord, 3));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.45);" NL " v = imageLoad(g_image_cube, ivec3(coord, 4));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.5);" NL " v = imageLoad(g_image_cube, ivec3(coord, 5));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.55);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 0));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.05);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 1));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 2));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.15);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 3));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 4));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.25);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 5));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 6));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.35);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 7));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.4);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 8));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.45);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 9));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.5);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 10));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.55);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, 11));" NL " if (v != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.6);" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.3.2.2 BasicAllTargetsLoadMS //----------------------------------------------------------------------------- class BasicAllTargetsLoadMS : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { if (!SupportedSamples(4)) return NOT_SUPPORTED; CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!ReadMS(GL_RGBA32F, vec4(-1.0f, 10.0f, -200.0f, 3000.0f), vec4(-1.0f, 10.0f, -200.0f, 3000.0f))) return ERROR; GLint isamples; glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &isamples); if (isamples >= 4) { if (!ReadMS(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!ReadMS(GL_RGBA32UI, uvec4(1, 2, 3, 4), uvec4(1, 2, 3, 4))) return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template bool ReadMS(GLenum internalformat, const T& value, const T& expected_value) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSMS(internalformat, expected_value).c_str()); GLuint textures[2]; glGenTextures(2, textures); const int kSize = 16; glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures[0]); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, internalformat, kSize, kSize, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures[1]); glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, internalformat, kSize, kSize, 2, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textures[0], 0); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textures[1], 0, 0); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, textures[1], 0, 1); const GLenum draw_buffers[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, draw_buffers); ClearBuffer(GL_COLOR, 0, value); ClearBuffer(GL_COLOR, 1, value); ClearBuffer(GL_COLOR, 2, value); glDeleteFramebuffers(1, &fbo); glBindImageTexture(1, textures[0], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(4, textures[1], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_2dms"), 1); glUniform1i(glGetUniformLocation(program, "g_image_2dms_array"), 4); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; } std::map name_index_map; GLint uniforms; glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniforms); if (uniforms != 2) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "ACTIVE_UNIFORMS is " << uniforms << " should be 2." << tcu::TestLog::EndMessage; } for (GLuint index = 0; index < static_cast(uniforms); ++index) { GLchar name[32]; glGetActiveUniformName(program, index, sizeof(name), NULL, name); name_index_map.insert(std::make_pair(std::string(name), index)); } if (!CheckUniform(program, "g_image_2dms", name_index_map, 1, ImageType(GL_TEXTURE_2D_MULTISAMPLE))) status = false; if (!CheckUniform(program, "g_image_2dms_array", name_index_map, 1, ImageType(GL_TEXTURE_2D_MULTISAMPLE_ARRAY))) status = false; glDeleteTextures(2, textures); glUseProgram(0); glDeleteProgram(program); return status; } template std::string GenFSMS(GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DMSArray g_image_2dms_array;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " if (imageLoad(g_image_2dms, coord, 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 0), 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 0), 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 0), 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 0), 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 1), 0) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 1), 1) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 1), 2) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (imageLoad(g_image_2dms_array, ivec3(coord, 1), 3) != " << TypePrefix() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // 1.3.3 BasicAllTargetsAtomic //----------------------------------------------------------------------------- class BasicAllTargetsAtomic : public ShaderImageLoadStoreBase { GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { CreateFullViewportQuad(&m_vao, &m_vbo, NULL); if (!Atomic(GL_R32I)) return ERROR; if (!Atomic(GL_R32UI)) return ERROR; if (!AtomicCube(GL_R32I)) return ERROR; if (!AtomicCube(GL_R32UI)) return ERROR; GLint isamples; glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &isamples); if (SupportedSamples(4) && isamples >= 4) { if (!AtomicMS(GL_R32I)) return ERROR; if (!AtomicMS(GL_R32UI)) return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); return NO_ERROR; } template bool Atomic(GLenum internalformat) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFS(internalformat).c_str()); GLuint textures[7]; GLuint buffer; glGenTextures(7, textures); glGenBuffers(1, &buffer); const int kSize = 16; std::vector data(kSize * kSize * 2); glBindTexture(GL_TEXTURE_1D, textures[0]); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage1D(GL_TEXTURE_1D, 0, internalformat, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D, 0); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_3D, textures[2]); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage3D(GL_TEXTURE_3D, 0, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_3D, 0); glBindTexture(GL_TEXTURE_RECTANGLE, textures[3]); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_RECTANGLE, 0); glBindBuffer(GL_TEXTURE_BUFFER, buffer); glBufferData(GL_TEXTURE_BUFFER, kSize * sizeof(T), &data[0], GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_BUFFER, textures[4]); glTexBuffer(GL_TEXTURE_BUFFER, internalformat, buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_1D_ARRAY, textures[5]); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, internalformat, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_1D_ARRAY, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, textures[6]); 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, internalformat, kSize, kSize, 2, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(1, textures[1], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(2, textures[2], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(3, textures[3], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(4, textures[4], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(5, textures[5], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(6, textures[6], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_1d"), 0); glUniform1i(glGetUniformLocation(program, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program, "g_image_buffer"), 4); glUniform1i(glGetUniformLocation(program, "g_image_1darray"), 5); glUniform1i(glGetUniformLocation(program, "g_image_2darray"), 6); glBindVertexArray(m_vao); glViewport(0, 0, kSize, 1); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, 1, vec4(0, 1, 0, 1))) { status = false; } glUseProgram(0); glDeleteProgram(program); glDeleteTextures(7, textures); glDeleteBuffers(1, &buffer); return status; } template bool AtomicCube(GLenum internalformat) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSCube(internalformat).c_str()); GLuint textures[2]; glGenTextures(2, textures); const int kSize = 16; std::vector data(kSize * kSize * 12); glBindTexture(GL_TEXTURE_CUBE_MAP, textures[0]); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, textures[1]); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, kSize, kSize, 12, 0, Format(), Type(), &data[0]); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); glBindImageTexture(0, textures[0], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(1, textures[1], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_cube"), 0); glUniform1i(glGetUniformLocation(program, "g_image_cube_array"), 1); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; } glUseProgram(0); glDeleteProgram(program); glDeleteTextures(2, textures); return status; } template bool AtomicMS(GLenum internalformat) { const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const GLuint program = BuildProgram(src_vs, NULL, NULL, NULL, GenFSMS(internalformat).c_str()); GLuint textures[2]; glGenTextures(2, textures); const int kSize = 16; glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures[0]); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, internalformat, kSize, kSize, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, textures[1]); glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, internalformat, kSize, kSize, 2, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textures[0], 0); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textures[1], 0, 0); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, textures[1], 0, 1); const GLenum draw_buffers[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, draw_buffers); if (internalformat == GL_R32I) { const GLint value[4] = { 0, 0, 0, 0 }; glClearBufferiv(GL_COLOR, 0, value); glClearBufferiv(GL_COLOR, 1, value); glClearBufferiv(GL_COLOR, 2, value); } else { const GLuint value[4] = { 0, 0, 0, 0 }; glClearBufferuiv(GL_COLOR, 0, value); glClearBufferuiv(GL_COLOR, 1, value); glClearBufferuiv(GL_COLOR, 2, value); } glDeleteFramebuffers(1, &fbo); glBindImageTexture(1, textures[0], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(4, textures[1], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_2dms"), 1); glUniform1i(glGetUniformLocation(program, "g_image_2dms_array"), 4); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(m_vao); glViewport(0, 0, kSize, kSize); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); bool status = true; if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { status = false; } glDeleteTextures(2, textures); glUseProgram(0); glDeleteProgram(program); return status; } template std::string GenFS(GLenum internalformat) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " if (imageAtomicAdd(g_image_1d, coord.x, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_1d, coord.x, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_1d, coord.x, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_1d, coord.x, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_1d, coord.x, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_1d, coord.x, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1d, coord.x, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_1d, coord.x, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1d, coord.x, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_2d, coord, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2d, coord, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2d, coord, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2d, coord, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2d, coord, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2d, coord, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2d, coord, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2d, coord, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2d, coord, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_3d, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_3d, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_3d, ivec3(coord, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_3d, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_3d, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_3d, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_3d, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_3d, ivec3(coord, 0), 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicExchange(g_image_3d, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_2drect, coord, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2drect, coord, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2drect, coord, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2drect, coord, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2drect, coord, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2drect, coord, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2drect, coord, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2drect, coord, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2drect, coord, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_buffer, coord.x, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_buffer, coord.x, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_buffer, coord.x, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_buffer, coord.x, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_buffer, coord.x, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_buffer, coord.x, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_buffer, coord.x, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_buffer, coord.x, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_buffer, coord.x, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_1darray, ivec2(coord.x, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_1darray, ivec2(coord.x, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_1darray, ivec2(coord.x, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_1darray, ivec2(coord.x, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_1darray, ivec2(coord.x, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_1darray, ivec2(coord.x, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1darray, ivec2(coord.x, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicCompSwap(g_image_1darray, ivec2(coord.x, 0), 1, 6) != 1) o_color = " "vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1darray, ivec2(coord.x, 0), " "0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_2darray, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2darray, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2darray, ivec3(coord, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2darray, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2darray, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2darray, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2darray, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicCompSwap(g_image_2darray, ivec3(coord, 0), 1, 6) != 1) o_color = vec4(1.0, " "0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2darray, ivec3(coord, 0), 0) != 6) " "o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; return os.str(); } template std::string GenFSCube(GLenum internalformat) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "imageCubeArray g_image_cube_array;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " if (imageAtomicAdd(g_image_cube, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_cube, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_cube, ivec3(coord, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_cube, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_cube, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_cube, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_cube, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_cube, ivec3(coord, 0), 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicExchange(g_image_cube, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image_cube_array, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicMin(g_image_cube_array, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, " "0.0, 1.0);" NL " if (imageAtomicMax(g_image_cube_array, ivec3(coord, 0), 4) != 2) o_color " "= vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_cube_array, " "ivec3(coord, 0), 0) != 4) o_color = " "vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_cube_array, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_cube_array, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicExchange(g_image_cube_array, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, " "0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_cube_array, ivec3(coord, 0), 1, 6) != " "1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_cube_array, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL "}"; return os.str(); } template std::string GenFSMS(GLenum internalformat) { std::ostringstream os; os << "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DMSArray g_image_2dms_array;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " if (imageAtomicAdd(g_image_2dms, coord, 1, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2dms, coord, 1, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2dms, coord, 1, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2dms, coord, 1, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2dms, coord, 1, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2dms, coord, 1, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2dms, coord, 1, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2dms, coord, 1, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2dms, coord, 1, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL NL " if (imageAtomicAdd(g_image_2dms_array, ivec3(coord, 1), 1, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicMin(g_image_2dms_array, ivec3(coord, 1), 1, 3) != 2) o_color = vec4(1.0, " "0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2dms_array, ivec3(coord, 1), 1, 4) != 2) " "o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2dms_array, ivec3(coord, 1), 1, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicOr(g_image_2dms_array, ivec3(coord, 1), 1, 7) != 0) o_color = vec4(1.0, " "0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2dms_array, ivec3(coord, 1), 1, 4) != 7) " "o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2dms_array, ivec3(coord, 1), 1, 1) != 3) o_color = vec4(1.0, 0.0, " "0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2dms_array, ivec3(coord, 1), 1, 1, 6) != 1) o_color = " "vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2dms_array, " "ivec3(coord, 1), 1, 0) != 6) o_color = vec4(1.0, 0.0, " "0.0, 1.0);" NL "}"; return os.str(); } }; //----------------------------------------------------------------------------- // LoadStoreMachine //----------------------------------------------------------------------------- class LoadStoreMachine : public ShaderImageLoadStoreBase { GLuint m_vao; int m_stage; virtual long Setup() { glGenVertexArrays(1, &m_vao); return NO_ERROR; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } template bool Write(GLenum internalformat, const T& write_value, const T& expected_value) { const GLenum targets[] = { GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_CUBE_MAP_ARRAY }; const int kTargets = sizeof(targets) / sizeof(targets[0]); GLuint program_store = 0; GLuint program_load = 0; if (m_stage == 0) { // VS program_store = BuildProgram(GenStoreShader(m_stage, internalformat, write_value).c_str(), NULL, NULL, NULL, NULL); program_load = BuildProgram(GenLoadShader(m_stage, internalformat, expected_value).c_str(), NULL, NULL, NULL, NULL); } else if (m_stage == 1) { // TCS const char* const glsl_vs = "#version 420 core" NL "void main() {}"; const char* const glsl_tes = "#version 420 core" NL "layout(quads, point_mode) in;" NL "void main() {}"; program_store = BuildProgram(glsl_vs, GenStoreShader(m_stage, internalformat, write_value).c_str(), glsl_tes, NULL, NULL); program_load = BuildProgram(glsl_vs, GenLoadShader(m_stage, internalformat, expected_value).c_str(), glsl_tes, NULL, NULL); } else if (m_stage == 2) { // TES const char* const glsl_vs = "#version 420 core" NL "void main() {}"; program_store = BuildProgram(glsl_vs, NULL, GenStoreShader(m_stage, internalformat, write_value).c_str(), NULL, NULL); program_load = BuildProgram(glsl_vs, NULL, GenLoadShader(m_stage, internalformat, expected_value).c_str(), NULL, NULL); } else if (m_stage == 3) { // GS const char* const glsl_vs = "#version 420 core" NL "void main() {}"; program_store = BuildProgram(glsl_vs, NULL, NULL, GenStoreShader(m_stage, internalformat, write_value).c_str(), NULL); program_load = BuildProgram(glsl_vs, NULL, NULL, GenLoadShader(m_stage, internalformat, expected_value).c_str(), NULL); } else if (m_stage == 4) { // CS { std::string source = GenStoreShader(m_stage, internalformat, write_value); const char* const src = source.c_str(); GLuint sh = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(sh, 1, &src, NULL); glCompileShader(sh); program_store = glCreateProgram(); glAttachShader(program_store, sh); glLinkProgram(program_store); glDeleteShader(sh); } { std::string source = GenLoadShader(m_stage, internalformat, expected_value); const char* const src = source.c_str(); GLuint sh = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(sh, 1, &src, NULL); glCompileShader(sh); program_load = glCreateProgram(); glAttachShader(program_load, sh); glLinkProgram(program_load); glDeleteShader(sh); } } GLuint textures[kTargets], texture_result; glGenTextures(kTargets, textures); glGenTextures(1, &texture_result); glBindTexture(GL_TEXTURE_2D, texture_result); 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_RGBA32F, 1, 1); for (int i = 0; i < kTargets; ++i) { glBindTexture(targets[i], textures[i]); glTexParameteri(targets[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(targets[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST); if (targets[i] == GL_TEXTURE_1D) { glTexStorage1D(targets[i], 1, internalformat, 1); } else if (targets[i] == GL_TEXTURE_2D || targets[i] == GL_TEXTURE_RECTANGLE) { glTexStorage2D(targets[i], 1, internalformat, 1, 1); } else if (targets[i] == GL_TEXTURE_3D || targets[i] == GL_TEXTURE_2D_ARRAY) { glTexStorage3D(targets[i], 1, internalformat, 1, 1, 2); } else if (targets[i] == GL_TEXTURE_CUBE_MAP) { glTexStorage2D(targets[i], 1, internalformat, 1, 1); } else if (targets[i] == GL_TEXTURE_CUBE_MAP_ARRAY) { glTexStorage3D(targets[i], 1, internalformat, 1, 1, 12); } else if (targets[i] == GL_TEXTURE_1D_ARRAY) { glTexStorage2D(targets[i], 1, internalformat, 1, 2); } } glBindImageTexture(0, textures[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(1, textures[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(2, textures[2], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(3, textures[3], 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(4, textures[4], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(5, textures[5], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(6, textures[6], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glBindImageTexture(7, textures[7], 0, GL_TRUE, 0, GL_WRITE_ONLY, internalformat); glUseProgram(program_store); glUniform1i(glGetUniformLocation(program_store, "g_image_1d"), 0); glUniform1i(glGetUniformLocation(program_store, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program_store, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program_store, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program_store, "g_image_cube"), 4); glUniform1i(glGetUniformLocation(program_store, "g_image_1darray"), 5); glUniform1i(glGetUniformLocation(program_store, "g_image_2darray"), 6); glUniform1i(glGetUniformLocation(program_store, "g_image_cube_array"), 7); glBindVertexArray(m_vao); if (m_stage == 1 || m_stage == 2) { // TCS or TES glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, 1); glPatchParameteri(GL_PATCH_VERTICES, 3); } else if (m_stage == 4) { // CS glDispatchCompute(1, 1, 1); } else { glDrawArrays(GL_POINTS, 0, 1); } bool status = true; for (int i = 0; i < kTargets; ++i) { glBindTexture(targets[i], textures[i]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); if (targets[i] == GL_TEXTURE_CUBE_MAP) { for (int face = 0; face < 6; ++face) { T data; glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, Format(), Type(), &data[0]); if (!Equal(data, expected_value, internalformat)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value is: " << ToString(data) << ". Value should be: " << ToString(expected_value) << ". Format is: " << FormatEnumToString(internalformat) << ". Target is: " << EnumToString(targets[i]) << ". Stage is: " << StageName(m_stage) << tcu::TestLog::EndMessage; } } } else { T data[12]; for (deUint32 ndx = 0; ndx < DE_LENGTH_OF_ARRAY(data); ndx++) data[ndx] = T(0); glGetTexImage(targets[i], 0, Format(), Type(), &data[0]); int count = 1; if (targets[i] == GL_TEXTURE_3D || targets[i] == GL_TEXTURE_2D_ARRAY) count = 2; else if (targets[i] == GL_TEXTURE_CUBE_MAP_ARRAY) count = 12; else if (targets[i] == GL_TEXTURE_1D_ARRAY) count = 2; for (int j = 0; j < count; ++j) { if (!Equal(data[j], expected_value, internalformat)) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "Value is: " << ToString(data[j]) << ". Value should be: " << ToString(expected_value) << ". Format is: " << FormatEnumToString(internalformat) << ". Target is: " << EnumToString(targets[i]) << ". Stage is: " << StageName(m_stage) << tcu::TestLog::EndMessage; } } } } glBindImageTexture(0, texture_result, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glBindImageTexture(1, textures[1], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(2, textures[2], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(3, textures[3], 0, GL_FALSE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(4, textures[4], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(5, textures[5], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(6, textures[6], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glBindImageTexture(7, textures[7], 0, GL_TRUE, 0, GL_READ_ONLY, internalformat); glUseProgram(program_load); glUniform1i(glGetUniformLocation(program_load, "g_image_result"), 0); glUniform1i(glGetUniformLocation(program_load, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program_load, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program_load, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program_load, "g_image_cube"), 4); glUniform1i(glGetUniformLocation(program_load, "g_image_1darray"), 5); glUniform1i(glGetUniformLocation(program_load, "g_image_2darray"), 6); glUniform1i(glGetUniformLocation(program_load, "g_image_cube_array"), 7); if (m_stage == 1 || m_stage == 2) { // TCS or TES glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, 1); glPatchParameteri(GL_PATCH_VERTICES, 3); } else if (m_stage == 4) { // CS glDispatchCompute(1, 1, 1); } else { glDrawArrays(GL_POINTS, 0, 1); } { vec4 color; glBindTexture(GL_TEXTURE_2D, texture_result); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &color[0]); if (!tcu::allEqual(color, vec4(0, 1, 0, 1))) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is: " << ToString(color) << ". Format is: " << FormatEnumToString(internalformat) << ". Stage is: " << StageName(m_stage) << tcu::TestLog::EndMessage; } } glUseProgram(0); glDeleteProgram(program_store); glDeleteProgram(program_load); glDeleteTextures(kTargets, textures); glDeleteTextures(1, &texture_result); return status; } template std::string GenStoreShader(int stage, GLenum internalformat, const T& write_value) { std::ostringstream os; os << "#version 420 core"; if (stage == 4) { // CS os << NL "#extension GL_ARB_compute_shader : require"; } os << NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix() << "imageCubeArray g_image_cube_array;" NL "uniform " << TypePrefix() << "vec4 g_value = " << TypePrefix() << "vec4" << write_value << ";" NL "uniform int g_index[6] = int[](0, 1, 2, 3, 4, 5);"; if (stage == 0) { // VS os << NL "void main() {" NL " ivec2 coord = ivec2(gl_VertexID, g_index[0]);"; } else if (stage == 1) { // TCS os << NL "layout(vertices = 1) out;" NL "void main() {" NL " gl_TessLevelInner[0] = 1;" NL " gl_TessLevelInner[1] = 1;" NL " gl_TessLevelOuter[0] = 1;" NL " gl_TessLevelOuter[1] = 1;" NL " gl_TessLevelOuter[2] = 1;" NL " gl_TessLevelOuter[3] = 1;" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_index[0]);"; } else if (stage == 2) { // TES os << NL "layout(quads, point_mode) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_index[0]);"; } else if (stage == 3) { // GS os << NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveIDIn, g_index[0]);"; } else if (stage == 4) { // CS os << NL "layout(local_size_x = 1) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_GlobalInvocationID.x, g_index[0]);"; } os << NL " imageStore(g_image_1d, coord.x, g_value);" NL " imageStore(g_image_2d, coord, g_value);" NL " imageStore(g_image_3d, ivec3(coord.xy, g_index[0]), g_value);" NL " imageStore(g_image_3d, ivec3(coord.xy, g_index[1]), g_value);" NL " imageStore(g_image_2drect, coord, g_value);" NL " for (int i = 0; i < 6; ++i) {" NL " imageStore(g_image_cube, ivec3(coord, g_index[i]), g_value);" NL " }" NL " imageStore(g_image_1darray, ivec2(coord.x, g_index[0]), g_value);" NL " imageStore(g_image_1darray, ivec2(coord.x, g_index[1]), g_value);" NL " imageStore(g_image_2darray, ivec3(coord, g_index[0]), g_value);" NL " imageStore(g_image_2darray, ivec3(coord, g_index[1]), g_value);" NL " for (int i = 0; i < 6; ++i) {" NL " imageStore(g_image_cube_array, ivec3(coord, g_index[i]), g_value);" NL " }" NL " for (int i = 0; i < 6; ++i) {" NL " imageStore(g_image_cube_array, ivec3(coord, g_index[i] + 6), g_value);" NL " }" NL "}"; return os.str(); } template std::string GenLoadShader(int stage, GLenum internalformat, const T& expected_value) { std::ostringstream os; os << "#version 420 core"; if (stage == 4) { // CS os << NL "#extension GL_ARB_compute_shader : require"; } os << NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix() << "imageCubeArray g_image_cube_array;" NL "layout(rgba32f) writeonly uniform image2D g_image_result;" NL "uniform " << TypePrefix() << "vec4 g_value = " << TypePrefix() << "vec4" << expected_value << ";" NL "uniform int g_index[6] = int[](0, 1, 2, 3, 4, 5);"; if (stage == 0) { // VS os << NL "void main() {" NL " ivec2 coord = ivec2(gl_VertexID, g_index[0]);"; } else if (stage == 1) { // TCS os << NL "layout(vertices = 1) out;" NL "void main() {" NL " gl_TessLevelInner[0] = 1;" NL " gl_TessLevelInner[1] = 1;" NL " gl_TessLevelOuter[0] = 1;" NL " gl_TessLevelOuter[1] = 1;" NL " gl_TessLevelOuter[2] = 1;" NL " gl_TessLevelOuter[3] = 1;" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_index[0]);"; } else if (stage == 2) { // TES os << NL "layout(quads, point_mode) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_index[0]);"; } else if (stage == 3) { // GS os << NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveIDIn, g_index[0]);"; } else if (stage == 4) { // CS os << NL "layout(local_size_x = 1) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_GlobalInvocationID.x, g_index[0]);"; } os << NL " vec4 r = vec4(0, 1, 0, 1);" NL " " << TypePrefix() << "vec4 v;" NL " v = imageLoad(g_image_2d, coord);" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_3d, ivec3(coord, g_index[0]));" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_2drect, coord);" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_cube, ivec3(coord, g_index[0]));" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_1darray, coord);" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_2darray, ivec3(coord, g_index[0]));" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " v = imageLoad(g_image_cube_array, ivec3(coord, g_index[0]));" NL " if (v != g_value) r = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageStore(g_image_result, coord, r);" NL "}"; return os.str(); } protected: long RunStage(int stage) { if (!SupportedInStage(stage, 8)) return NOT_SUPPORTED; glEnable(GL_RASTERIZER_DISCARD); m_stage = stage; if (!Write(GL_RGBA32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R32F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 4.0f))) return ERROR; if (!Write(GL_RG16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG32I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R32I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_R11F_G11F_B10F, vec4(1.0f, 2.0f, 3.0f, 4.0f), vec4(1.0f, 2.0f, 3.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG16I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R16I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 3, -4))) return ERROR; if (!Write(GL_RG8I, ivec4(1, -2, 3, -4), ivec4(1, -2, 0, 1))) return ERROR; if (!Write(GL_R8I, ivec4(1, -2, 3, -4), ivec4(1, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RGB10_A2UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG32UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R32UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG16UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R16UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 2, 3))) return ERROR; if (!Write(GL_RG8UI, uvec4(0, 1, 2, 3), uvec4(0, 1, 0, 1))) return ERROR; if (!Write(GL_R8UI, uvec4(7, 2, 3, 4), uvec4(7, 0, 0, 1))) return ERROR; if (!Write(GL_RGBA16, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RGB10_A2, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG16, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8, vec4(1.0f), vec4(1.0f))) return ERROR; if (!Write(GL_RG8, vec4(1.0f), vec4(1.0f, 1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8, vec4(1.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; // { if (!Write(GL_RGBA16_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG16_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R16_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_RGBA8_SNORM, vec4(1.0f, -1.0f, 1.0f, -1.0f), vec4(1.0f, -1.0f, 1.0f, -1.0f))) return ERROR; if (!Write(GL_RG8_SNORM, vec4(-1.0f), vec4(-1.0f, -1.0f, 0.0f, 1.0f))) return ERROR; if (!Write(GL_R8_SNORM, vec4(-1.0f, 1.0f, -1.0f, 1.0f), vec4(-1.0f, 0.0f, 0.0f, 1.0f))) return ERROR; } return NO_ERROR; } }; //----------------------------------------------------------------------------- // AtomicMachine //----------------------------------------------------------------------------- class AtomicMachine : public ShaderImageLoadStoreBase { GLuint m_vao; virtual long Setup() { glEnable(GL_RASTERIZER_DISCARD); glGenVertexArrays(1, &m_vao); return NO_ERROR; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } template bool Atomic(int stage, GLenum internalformat) { GLuint program = 0; if (stage == 0) { // VS program = BuildProgram(GenShader(stage, internalformat).c_str(), NULL, NULL, NULL, NULL); } else if (stage == 1) { // TCS const char* const glsl_vs = "#version 420 core" NL "void main() {}"; const char* const glsl_tes = "#version 420 core" NL "layout(quads, point_mode) in;" NL "void main() {}"; program = BuildProgram(glsl_vs, GenShader(stage, internalformat).c_str(), glsl_tes, NULL, NULL); } else if (stage == 2) { // TES const char* const glsl_vs = "#version 420 core" NL "void main() {}"; program = BuildProgram(glsl_vs, NULL, GenShader(stage, internalformat).c_str(), NULL, NULL); } else if (stage == 3) { // GS const char* const glsl_vs = "#version 420 core" NL "void main() {}"; program = BuildProgram(glsl_vs, NULL, NULL, GenShader(stage, internalformat).c_str(), NULL); } else if (stage == 4) { // CS std::string source = GenShader(stage, internalformat); const char* const src = source.c_str(); GLuint sh = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(sh, 1, &src, NULL); glCompileShader(sh); program = glCreateProgram(); glAttachShader(program, sh); glLinkProgram(program); glDeleteShader(sh); } GLuint texture_result; glGenTextures(1, &texture_result); glBindTexture(GL_TEXTURE_2D, texture_result); 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_RGBA32F, 1, 1); const GLenum targets[] = { GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BUFFER, GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY }; const int kTargets = sizeof(targets) / sizeof(targets[0]); GLuint textures[kTargets]; GLuint buffer; glGenTextures(kTargets, textures); glGenBuffers(1, &buffer); for (int i = 0; i < kTargets; ++i) { glBindTexture(targets[i], textures[i]); if (targets[i] != GL_TEXTURE_BUFFER) { glTexParameteri(targets[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(targets[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if (targets[i] == GL_TEXTURE_2D || targets[i] == GL_TEXTURE_RECTANGLE) { glTexStorage2D(targets[i], 1, internalformat, 1, 1); } else if (targets[i] == GL_TEXTURE_3D || targets[i] == GL_TEXTURE_2D_ARRAY) { glTexStorage3D(targets[i], 1, internalformat, 1, 1, 2); } else if (targets[i] == GL_TEXTURE_CUBE_MAP) { glTexStorage2D(targets[i], 1, internalformat, 1, 1); } else if (targets[i] == GL_TEXTURE_BUFFER) { glBindBuffer(GL_TEXTURE_BUFFER, buffer); glBufferData(GL_TEXTURE_BUFFER, 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glTexBuffer(targets[i], internalformat, buffer); } else if (targets[i] == GL_TEXTURE_1D_ARRAY) { glTexStorage2D(targets[i], 1, internalformat, 1, 2); } } glBindImageTexture(0, texture_result, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glBindImageTexture(1, textures[0], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(2, textures[1], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(3, textures[2], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(4, textures[3], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(5, textures[4], 0, GL_FALSE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(6, textures[5], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glBindImageTexture(7, textures[6], 0, GL_TRUE, 0, GL_READ_WRITE, internalformat); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "g_image_result"), 0); glUniform1i(glGetUniformLocation(program, "g_image_2d"), 1); glUniform1i(glGetUniformLocation(program, "g_image_3d"), 2); glUniform1i(glGetUniformLocation(program, "g_image_2drect"), 3); glUniform1i(glGetUniformLocation(program, "g_image_cube"), 4); glUniform1i(glGetUniformLocation(program, "g_image_buffer"), 5); glUniform1i(glGetUniformLocation(program, "g_image_1darray"), 6); glUniform1i(glGetUniformLocation(program, "g_image_2darray"), 7); glBindVertexArray(m_vao); if (stage == 1 || stage == 2) { // TCS or TES glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, 1); glPatchParameteri(GL_PATCH_VERTICES, 3); } else if (stage == 4) { // CS glDispatchCompute(1, 1, 1); } else { glDrawArrays(GL_POINTS, 0, 1); } bool status = true; { vec4 color; glBindTexture(GL_TEXTURE_2D, texture_result); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &color[0]); if (!tcu::allEqual(color, vec4(0, 1, 0, 1))) { status = false; m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is: " << ToString(color) << ". Format is: " << FormatEnumToString(internalformat) << ". Stage is: " << StageName(stage) << tcu::TestLog::EndMessage; } } glUseProgram(0); glDeleteProgram(program); glDeleteTextures(7, textures); glDeleteTextures(1, &texture_result); glDeleteBuffers(1, &buffer); return status; } template std::string GenShader(int stage, GLenum internalformat) { std::ostringstream os; os << "#version 420 core"; if (stage == 4) { // CS os << NL "#extension GL_ARB_compute_shader : require"; } os << NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix() << "image2DArray g_image_2darray;" NL "layout(rgba32f) writeonly uniform image2D g_image_result;" NL "uniform int g_value[6] = int[](0, 1, 2, 3, 4, 5);"; if (stage == 0) { // VS os << NL "void main() {" NL " ivec2 coord = ivec2(gl_VertexID, g_value[0]);"; } else if (stage == 1) { // TCS os << NL "layout(vertices = 1) out;" NL "void main() {" NL " gl_TessLevelInner[0] = 1;" NL " gl_TessLevelInner[1] = 1;" NL " gl_TessLevelOuter[0] = 1;" NL " gl_TessLevelOuter[1] = 1;" NL " gl_TessLevelOuter[2] = 1;" NL " gl_TessLevelOuter[3] = 1;" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_value[0]);"; } else if (stage == 2) { // TES os << NL "layout(quads, point_mode) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveID, g_value[0]);"; } else if (stage == 3) { // GS os << NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "void main() {" NL " ivec2 coord = ivec2(gl_PrimitiveIDIn, g_value[0]);"; } else if (stage == 4) { // CS os << NL "layout(local_size_x = 1) in;" NL "void main() {" NL " ivec2 coord = ivec2(gl_GlobalInvocationID.x, g_value[0]);"; } os << NL " vec4 o_color = vec4(0, 1, 0, 1);" NL " imageAtomicExchange(g_image_2d, coord, 0);" NL " if (imageAtomicAdd(g_image_2d, coord, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2d, coord, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2d, coord, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2d, coord, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2d, coord, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2d, coord, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2d, coord, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2d, coord, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2d, coord, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_3d, ivec3(coord, 0), 0);" NL " if (imageAtomicAdd(g_image_3d, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_3d, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_3d, ivec3(coord, g_value[0]), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_3d, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_3d, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_3d, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_3d, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_3d, ivec3(coord, 0), 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_3d, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_2drect, coord, 0);" NL " if (imageAtomicAdd(g_image_2drect, coord, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2drect, coord, 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2drect, coord, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2drect, coord, 0) != g_value[4]) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2drect, coord, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2drect, coord, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2drect, coord, 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2drect, coord, g_value[1], 6) != 1) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicExchange(g_image_2drect, coord, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_cube, ivec3(coord, 0), 0);" NL " if (imageAtomicAdd(g_image_cube, " "ivec3(coord, 0), g_value[2]) != 0) " "o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_cube, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_cube, ivec3(coord, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_cube, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_cube, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_cube, ivec3(coord, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_cube, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_cube, ivec3(coord, g_value[0]), 1, 6) != 1) o_color = vec4(1.0, 0.0, " "0.0, 1.0);" NL " if (imageAtomicExchange(g_image_cube, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_buffer, coord.x, g_value[0]);" NL " if (imageAtomicAdd(g_image_buffer, coord.x, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_buffer, coord.x, g_value[3]) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_buffer, coord.x, 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_buffer, coord.x, 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_buffer, coord.x, 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_buffer, coord.x, 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_buffer, coord.x, g_value[1]) != 3) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicCompSwap(g_image_buffer, coord.x, 1, 6) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_buffer, coord.x, 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_1darray, ivec2(coord.x, 0), 0);" NL " if (imageAtomicAdd(g_image_1darray, ivec2(coord.x, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_1darray, ivec2(coord.x, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_1darray, ivec2(coord.x, g_value[0]), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicAnd(g_image_1darray, ivec2(coord.x, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_1darray, ivec2(coord.x, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_1darray, ivec2(coord.x, 0), 4) != 7) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1darray, ivec2(coord.x, 0), 1) != 3) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicCompSwap(g_image_1darray, ivec2(coord.x, 0), 1, 6) != 1) o_color = vec4(1.0, " "0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_1darray, ivec2(coord.x, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageAtomicExchange(g_image_2darray, ivec3(coord, 0), 0);" NL " if (imageAtomicAdd(g_image_2darray, ivec3(coord, 0), 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMin(g_image_2darray, ivec3(coord, 0), 3) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicMax(g_image_2darray, ivec3(coord, 0), 4) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAnd(g_image_2darray, ivec3(coord, 0), 0) != 4) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicOr(g_image_2darray, ivec3(coord, 0), 7) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicXor(g_image_2darray, ivec3(coord, 0), g_value[4]) != 7) o_color = vec4(1.0, 0.0, 0.0, " "1.0);" NL " if (imageAtomicExchange(g_image_2darray, ivec3(coord, 0), 1) != 3) o_color = vec4(1.0, 0.0, " "0.0, 1.0);" NL " if (imageAtomicCompSwap(g_image_2darray, ivec3(coord, 0), 1, 6) != 1) " "o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicExchange(g_image_2darray, ivec3(coord, 0), 0) != 6) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " imageStore(g_image_result, coord, o_color);" NL "}"; return os.str(); } protected: long RunStage(int stage) { if (!SupportedInStage(stage, 8)) return NOT_SUPPORTED; if (!Atomic(stage, GL_R32I)) return ERROR; if (!Atomic(stage, GL_R32UI)) return ERROR; return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.3.4 BasicAllTargetsLoadStoreVS //----------------------------------------------------------------------------- class BasicAllTargetsLoadStoreVS : public LoadStoreMachine { virtual long Run() { return RunStage(0); } }; //----------------------------------------------------------------------------- // 1.3.5 BasicAllTargetsLoadStoreTCS //----------------------------------------------------------------------------- class BasicAllTargetsLoadStoreTCS : public LoadStoreMachine { virtual long Run() { return RunStage(1); } }; //----------------------------------------------------------------------------- // 1.3.6 BasicAllTargetsLoadStoreTES //----------------------------------------------------------------------------- class BasicAllTargetsLoadStoreTES : public LoadStoreMachine { virtual long Run() { return RunStage(2); } }; //----------------------------------------------------------------------------- // 1.3.7 BasicAllTargetsLoadStoreGS //----------------------------------------------------------------------------- class BasicAllTargetsLoadStoreGS : public LoadStoreMachine { virtual long Run() { return RunStage(3); } }; //----------------------------------------------------------------------------- // 1.3.8 BasicAllTargetsLoadStoreCS //----------------------------------------------------------------------------- class BasicAllTargetsLoadStoreCS : public LoadStoreMachine { virtual long Run() { if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader")) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_ARB_compute_shader not supported, skipping test" << tcu::TestLog::EndMessage; return NO_ERROR; } return RunStage(4); } }; //----------------------------------------------------------------------------- // 1.3.9 BasicAllTargetsAtomicVS //----------------------------------------------------------------------------- class BasicAllTargetsAtomicVS : public AtomicMachine { virtual long Run() { return RunStage(0); } }; //----------------------------------------------------------------------------- // 1.3.10 BasicAllTargetsAtomicTCS //----------------------------------------------------------------------------- class BasicAllTargetsAtomicTCS : public AtomicMachine { virtual long Run() { return RunStage(1); } }; //----------------------------------------------------------------------------- // 1.3.11 BasicAllTargetsAtomicTES //----------------------------------------------------------------------------- class BasicAllTargetsAtomicTES : public AtomicMachine { virtual long Run() { return RunStage(2); } }; //----------------------------------------------------------------------------- // 1.3.12 BasicAllTargetsAtomicGS //----------------------------------------------------------------------------- class BasicAllTargetsAtomicGS : public AtomicMachine { virtual long Run() { return RunStage(3); } }; //----------------------------------------------------------------------------- // 1.3.13 BasicAllTargetsAtomicCS //----------------------------------------------------------------------------- class BasicAllTargetsAtomicCS : public AtomicMachine { virtual long Run() { if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader")) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_ARB_compute_shader not supported, skipping test" << tcu::TestLog::EndMessage; return NO_ERROR; } return RunStage(4); } }; //----------------------------------------------------------------------------- // 1.4.1 BasicGLSLMisc //----------------------------------------------------------------------------- class BasicGLSLMisc : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_program; GLuint m_vao, m_vbo; virtual long Setup() { m_texture = 0; m_program = 0; m_vao = m_vbo = 0; return NO_ERROR; } virtual long Run() { const int kSize = 32; std::vector data(kSize * kSize * 4, vec4(0.0f)); glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA32F, kSize, kSize, 4, 0, GL_RGBA, GL_FLOAT, &data[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* src_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) coherent volatile restrict uniform image2D g_image_layer0;" NL "layout(rgba32f) volatile uniform image2D g_image_layer1;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image_layer0, coord, vec4(1.0));" NL " memoryBarrier();" NL " imageStore(g_image_layer0, coord, vec4(2.0));" NL " memoryBarrier();" NL " imageStore(g_image_layer0, coord, vec4(0.0, 1.0, 0.0, 1.0));" NL " memoryBarrier();" NL " o_color = imageLoad(g_image_layer0, coord) + imageLoad(g_image_layer1, coord);" NL "}"; m_program = BuildProgram(src_vs, NULL, NULL, NULL, src_fs); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture, 0, GL_FALSE, 1, GL_READ_ONLY, GL_RGBA32F); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, kSize, kSize); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_image_layer0"), 0); glUniform1i(glGetUniformLocation(m_program, "g_image_layer1"), 1); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (!ValidateReadBuffer(0, 0, kSize, kSize, vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteTextures(1, &m_texture); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); glUseProgram(0); glDeleteProgram(m_program); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.4.2 BasicGLSLEarlyFragTests //----------------------------------------------------------------------------- class BasicGLSLEarlyFragTests : public ShaderImageLoadStoreBase { GLuint m_texture[2]; GLuint m_program[2]; GLuint m_vao, m_vbo; virtual long Setup() { m_texture[0] = m_texture[1] = 0; m_program[0] = m_program[1] = 0; m_vao = m_vbo = 0; return NO_ERROR; } virtual long Run() { int ds = m_context.getRenderContext().getRenderTarget().getDepthBits(); const int kSize = 32; std::vector data(kSize * kSize); glGenTextures(2, m_texture); glBindTexture(GL_TEXTURE_2D, m_texture[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kSize, kSize, 0, GL_RGBA, GL_FLOAT, &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, m_texture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kSize, kSize, 0, GL_RGBA, GL_FLOAT, &data[0]); glBindTexture(GL_TEXTURE_2D, 0); const char* glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* glsl_early_frag_tests_fs = "#version 420 core" NL "layout(early_fragment_tests) in;" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) coherent uniform image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image, coord, vec4(1.0));" NL " o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; const char* glsl_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) coherent uniform image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image, coord, vec4(1.0));" NL " o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; m_program[0] = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_early_frag_tests_fs); m_program[1] = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glViewport(0, 0, kSize, kSize); glBindVertexArray(m_vao); glEnable(GL_DEPTH_TEST); glClearColor(0.0, 1.0f, 0.0, 1.0f); glClearDepthf(0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(m_program[0]); glUniform1i(glGetUniformLocation(m_program[0], "g_image"), 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(m_program[1]); glUniform1i(glGetUniformLocation(m_program[1], "g_image"), 1); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindTexture(GL_TEXTURE_2D, m_texture[0]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &data[0]); for (int i = 0; i < kSize * kSize; ++i) { if (IsEqual(data[i], vec4(1.0f)) && ds != 0) return ERROR; } glBindTexture(GL_TEXTURE_2D, m_texture[1]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &data[0]); for (int i = 0; i < kSize * kSize; ++i) { if (!IsEqual(data[i], vec4(1.0f)) && ds != 0) return ERROR; } return NO_ERROR; } virtual long Cleanup() { glDisable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepthf(1.0f); glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteTextures(2, m_texture); glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); glUseProgram(0); glDeleteProgram(m_program[0]); glDeleteProgram(m_program[1]); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 1.4.3 BasicGLSLConst //----------------------------------------------------------------------------- class BasicGLSLConst : public ShaderImageLoadStoreBase { GLuint m_program; GLuint m_vao, m_vbo; virtual long Setup() { m_program = 0; m_vao = m_vbo = 0; return NO_ERROR; } virtual long Run() { bool isAtLeast44Context = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 4)); const char* src_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; std::ostringstream src_fs; src_fs << "#version " << (isAtLeast44Context ? "440" : "420") << " core"; src_fs << NL "layout(location = 0) out vec4 o_color;" NL "uniform int MaxImageUnits;" NL "uniform int MaxCombinedShaderOutputResources;" NL "uniform int MaxImageSamples;" NL "uniform int MaxVertexImageUniforms;" NL "uniform int MaxTessControlImageUniforms;" NL "uniform int MaxTessEvaluationImageUniforms;" NL "uniform int MaxGeometryImageUniforms;" NL "uniform int MaxFragmentImageUniforms;" NL "uniform int MaxCombinedImageUniforms;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " if (gl_MaxImageUnits != MaxImageUnits) o_color = vec4(1.0, 0.0, 0.0, 0.1);"; if (isAtLeast44Context) src_fs << NL " if (gl_MaxCombinedShaderOutputResources != MaxCombinedShaderOutputResources) o_color = " "vec4(0.2, 0.0, 0.0, 0.2);"; else src_fs << NL " if (gl_MaxCombinedImageUnitsAndFragmentOutputs != MaxCombinedShaderOutputResources) " "o_color = vec4(0.2, 0.0, 0.0, 0.2);"; src_fs << NL " if (gl_MaxImageSamples != MaxImageSamples) o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL " if (gl_MaxVertexImageUniforms != MaxVertexImageUniforms) o_color = vec4(1.0, 0.0, 0.0, 0.4);" NL " if (gl_MaxTessControlImageUniforms != MaxTessControlImageUniforms) o_color = vec4(1.0, 0.0, 0.0, " "0.5);" NL " if (gl_MaxTessEvaluationImageUniforms != MaxTessEvaluationImageUniforms) o_color = vec4(1.0, " "0.0, 0.0, 0.6);" NL " if (gl_MaxGeometryImageUniforms != MaxGeometryImageUniforms) o_color = vec4(1.0, 0.0, 0.0, 0.7);" NL " if (gl_MaxFragmentImageUniforms != MaxFragmentImageUniforms) o_color = vec4(1.0, 0.0, 0.0, 0.8);" NL " if (gl_MaxCombinedImageUniforms != MaxCombinedImageUniforms) o_color = vec4(1.0, 0.0, 0.0, 0.9);" NL "}"; m_program = BuildProgram(src_vs, NULL, NULL, NULL, src_fs.str().c_str()); glUseProgram(m_program); GLint i; glGetIntegerv(GL_MAX_IMAGE_UNITS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxImageUnits"), i); glGetIntegerv(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, &i); glUniform1i(glGetUniformLocation(m_program, "MaxCombinedShaderOutputResources"), i); glGetIntegerv(GL_MAX_IMAGE_SAMPLES, &i); glUniform1i(glGetUniformLocation(m_program, "MaxImageSamples"), i); glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxVertexImageUniforms"), i); glGetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxTessControlImageUniforms"), i); glGetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxTessEvaluationImageUniforms"), i); glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxGeometryImageUniforms"), i); glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxFragmentImageUniforms"), i); glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &i); glUniform1i(glGetUniformLocation(m_program, "MaxCombinedImageUniforms"), i); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(m_vao); 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() { glDeleteVertexArrays(1, &m_vao); glDeleteBuffers(1, &m_vbo); glUseProgram(0); glDeleteProgram(m_program); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.1 AdvancedSyncImageAccess //----------------------------------------------------------------------------- class AdvancedSyncImageAccess : public ShaderImageLoadStoreBase { GLuint m_buffer; GLuint m_buffer_tex; GLuint m_store_program; GLuint m_draw_program; GLuint m_attribless_vao; virtual long Setup() { m_buffer = 0; m_buffer_tex = 0; m_store_program = 0; m_draw_program = 0; m_attribless_vao = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; const char* const glsl_store_vs = "#version 420 core" NL "writeonly uniform imageBuffer g_output_data;" NL "void main() {" NL " vec2[4] data = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));" NL " imageStore(g_output_data, gl_VertexID, vec4(data[gl_VertexID], 0.0, 1.0));" NL "}"; const char* const glsl_draw_vs = "#version 420 core" NL "out vec4 vs_color;" NL "layout(rg32f) readonly uniform imageBuffer g_image;" NL "uniform samplerBuffer g_sampler;" NL "void main() {" NL " vec4 pi = imageLoad(g_image, gl_VertexID);" NL " vec4 ps = texelFetch(g_sampler, gl_VertexID);" NL " if (pi != ps) vs_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " else vs_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " gl_Position = pi;" NL "}"; const char* const glsl_draw_fs = "#version 420 core" NL "in vec4 vs_color;" NL "layout(location = 0) out vec4 o_color;" NL "void main() {" NL " o_color = vs_color;" NL "}"; m_store_program = BuildProgram(glsl_store_vs, NULL, NULL, NULL, NULL); m_draw_program = BuildProgram(glsl_draw_vs, NULL, NULL, NULL, glsl_draw_fs); glGenVertexArrays(1, &m_attribless_vao); glBindVertexArray(m_attribless_vao); glGenBuffers(1, &m_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec2) * 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_buffer); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RG32F); glEnable(GL_RASTERIZER_DISCARD); glUseProgram(m_store_program); glDrawArrays(GL_POINTS, 0, 4); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT); glDisable(GL_RASTERIZER_DISCARD); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_draw_program); 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() { glUseProgram(0); glDeleteBuffers(1, &m_buffer); glDeleteTextures(1, &m_buffer_tex); glDeleteProgram(m_store_program); glDeleteProgram(m_draw_program); glDeleteVertexArrays(1, &m_attribless_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.2 AdvancedSyncVertexArray //----------------------------------------------------------------------------- class AdvancedSyncVertexArray : public ShaderImageLoadStoreBase { GLuint m_position_buffer; GLuint m_color_buffer; GLuint m_element_buffer; GLuint m_position_buffer_tex; GLuint m_color_buffer_tex; GLuint m_element_buffer_tex; GLuint m_store_program; GLuint m_draw_program; GLuint m_attribless_vao; GLuint m_draw_vao; virtual long Setup() { m_position_buffer = 0; m_color_buffer = 0; m_element_buffer = 0; m_position_buffer_tex = 0; m_color_buffer_tex = 0; m_element_buffer_tex = 0; m_store_program = 0; m_draw_program = 0; m_attribless_vao = 0; m_draw_vao = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(3)) return NOT_SUPPORTED; const char* const glsl_store_vs = "#version 420 core" NL "layout(rg32f) writeonly uniform imageBuffer g_position_buffer;" NL "layout(rgba32f) writeonly uniform imageBuffer g_color_buffer;" NL "layout(r32ui) writeonly uniform uimageBuffer g_element_buffer;" NL "uniform vec4 g_color;" NL "void main() {" NL " vec2[4] data = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));" NL " imageStore(g_position_buffer, gl_VertexID, vec4(data[gl_VertexID], 0.0, 1.0));" NL " imageStore(g_color_buffer, gl_VertexID, g_color);" NL " imageStore(g_element_buffer, gl_VertexID, uvec4(gl_VertexID));" NL "}"; const char* const glsl_draw_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(location = 1) in vec4 i_color;" NL "out vec4 vs_color;" NL "void main() {" NL " gl_Position = i_position;" NL " vs_color = i_color;" NL "}"; const char* const glsl_draw_fs = "#version 420 core" NL "in vec4 vs_color;" NL "layout(location = 0) out vec4 o_color;" NL "void main() {" NL " o_color = vs_color;" NL "}"; m_store_program = BuildProgram(glsl_store_vs, NULL, NULL, NULL, NULL); glUseProgram(m_store_program); glUniform1i(glGetUniformLocation(m_store_program, "g_position_buffer"), 0); glUniform1i(glGetUniformLocation(m_store_program, "g_color_buffer"), 1); glUniform1i(glGetUniformLocation(m_store_program, "g_element_buffer"), 2); glUseProgram(0); m_draw_program = BuildProgram(glsl_draw_vs, NULL, NULL, NULL, glsl_draw_fs); glGenBuffers(1, &m_position_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_position_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec2) * 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenBuffers(1, &m_color_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_color_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec4) * 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenBuffers(1, &m_element_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_element_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(GLuint) * 4, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_position_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_position_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_position_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_color_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_color_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_color_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_element_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_element_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_element_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glGenVertexArrays(1, &m_attribless_vao); glGenVertexArrays(1, &m_draw_vao); glBindVertexArray(m_draw_vao); glBindBuffer(GL_ARRAY_BUFFER, m_position_buffer); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, m_color_buffer); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_element_buffer); glBindVertexArray(0); glEnable(GL_RASTERIZER_DISCARD); glBindImageTexture(0, m_position_buffer_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F); glBindImageTexture(1, m_color_buffer_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glBindImageTexture(2, m_element_buffer_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI); glUseProgram(m_store_program); glUniform4f(glGetUniformLocation(m_store_program, "g_color"), 0.0f, 1.0f, 0.0f, 1.0f); glBindVertexArray(m_attribless_vao); glDrawArrays(GL_POINTS, 0, 4); glDisable(GL_RASTERIZER_DISCARD); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_draw_program); glBindVertexArray(m_draw_vao); glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) { return ERROR; } glEnable(GL_RASTERIZER_DISCARD); glUseProgram(m_store_program); glUniform4f(glGetUniformLocation(m_store_program, "g_color"), 0.0f, 0.0f, 1.0f, 1.0f); glBindVertexArray(m_attribless_vao); glDrawArrays(GL_POINTS, 0, 4); glDisable(GL_RASTERIZER_DISCARD); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_draw_program); glBindVertexArray(m_draw_vao); glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 0, 1, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glUseProgram(0); glDeleteBuffers(1, &m_position_buffer); glDeleteBuffers(1, &m_color_buffer); glDeleteBuffers(1, &m_element_buffer); glDeleteTextures(1, &m_position_buffer_tex); glDeleteTextures(1, &m_color_buffer_tex); glDeleteTextures(1, &m_element_buffer_tex); glDeleteProgram(m_store_program); glDeleteProgram(m_draw_program); glDeleteVertexArrays(1, &m_attribless_vao); glDeleteVertexArrays(1, &m_draw_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.4 AdvancedSyncDrawIndirect //----------------------------------------------------------------------------- class AdvancedSyncDrawIndirect : public ShaderImageLoadStoreBase { GLuint m_draw_command_buffer; GLuint m_draw_command_buffer_tex; GLuint m_store_program; GLuint m_draw_program; GLuint m_attribless_vao; GLuint m_draw_vao; GLuint m_draw_vbo; virtual long Setup() { m_draw_command_buffer = 0; m_draw_command_buffer_tex = 0; m_store_program = 0; m_draw_program = 0; m_attribless_vao = 0; m_draw_vao = 0; m_draw_vbo = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; const char* const glsl_store_vs = "#version 420 core" NL "writeonly uniform uimageBuffer g_draw_command_buffer;" NL "void main() {" NL " imageStore(g_draw_command_buffer, 0, uvec4(4, 1, 0, 0));" NL "}"; const char* const glsl_draw_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_draw_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL "}"; m_store_program = BuildProgram(glsl_store_vs, NULL, NULL, NULL, NULL); m_draw_program = BuildProgram(glsl_draw_vs, NULL, NULL, NULL, glsl_draw_fs); glGenBuffers(1, &m_draw_command_buffer); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_draw_command_buffer); glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(uvec4), NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); glGenTextures(1, &m_draw_command_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_draw_command_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, m_draw_command_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glGenVertexArrays(1, &m_attribless_vao); CreateFullViewportQuad(&m_draw_vao, &m_draw_vbo, NULL); glBindImageTexture(0, m_draw_command_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32UI); glEnable(GL_RASTERIZER_DISCARD); glUseProgram(m_store_program); glBindVertexArray(m_attribless_vao); glDrawArrays(GL_POINTS, 0, 1); glDisable(GL_RASTERIZER_DISCARD); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_draw_program); glBindVertexArray(m_draw_vao); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m_draw_command_buffer); glMemoryBarrier(GL_COMMAND_BARRIER_BIT); glDrawArraysIndirect(GL_TRIANGLE_STRIP, 0); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); glDeleteBuffers(1, &m_draw_command_buffer); glDeleteTextures(1, &m_draw_command_buffer_tex); glDeleteProgram(m_store_program); glDeleteProgram(m_draw_program); glDeleteVertexArrays(1, &m_attribless_vao); glDeleteVertexArrays(1, &m_draw_vao); glDeleteBuffers(1, &m_draw_vbo); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.5 AdvancedSyncTextureUpdate //----------------------------------------------------------------------------- class AdvancedSyncTextureUpdate : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_store_program; GLuint m_draw_program; GLuint m_vao; GLuint m_vbo; GLuint m_pbo; virtual long Setup() { m_texture = 0; m_store_program = 0; m_draw_program = 0; m_vao = 0; m_vbo = 0; m_pbo = 0; return NO_ERROR; } virtual long Run() { const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "out vec2 vs_texcoord;" NL "void main() {" NL " gl_Position = i_position;" NL " vs_texcoord = 0.5 + 0.5 * i_position.xy;" NL "}"; const char* const glsl_store_fs = "#version 420 core" NL "writeonly uniform image2D g_image;" NL "void main() {" NL " imageStore(g_image, ivec2(gl_FragCoord.xy), gl_FragCoord);" NL " discard;" NL "}"; const char* const glsl_draw_fs = "#version 420 core" NL "in vec2 vs_texcoord;" NL "layout(location = 0) out vec4 o_color;" NL "uniform sampler2D g_sampler;" NL "void main() {" NL " o_color = texture(g_sampler, vs_texcoord);" NL "}"; m_store_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_store_fs); m_draw_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_draw_fs); std::vector data(16 * 16, vec4(1.0f)); glGenBuffers(1, &m_pbo); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER, 16 * 16 * sizeof(vec4), &data[0], GL_DYNAMIC_READ); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 16, 16, 0, GL_RGBA, GL_FLOAT, NULL); glBindTexture(GL_TEXTURE_2D, 0); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glViewport(0, 0, 16, 16); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glUseProgram(m_store_program); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo); glBindTexture(GL_TEXTURE_2D, m_texture); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 16, 16, GL_RGBA, GL_FLOAT, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glViewport(0, 0, getWindowWidth(), getWindowHeight()); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_draw_program); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(1, 1, 1, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_pbo); glDeleteTextures(1, &m_texture); glDeleteProgram(m_store_program); glDeleteProgram(m_draw_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.6 AdvancedSyncImageAccess2 //----------------------------------------------------------------------------- class AdvancedSyncImageAccess2 : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_store_program; GLuint m_draw_program; GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_texture = 0; m_store_program = 0; m_draw_program = 0; m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "out vec2 vs_texcoord;" NL "void main() {" NL " gl_Position = i_position;" NL " vs_texcoord = 0.5 + 0.5 * i_position.xy;" NL "}"; const char* const glsl_store_fs = "#version 420 core" NL "writeonly uniform image2D g_image;" NL "uniform vec4 g_color;" NL "void main() {" NL " imageStore(g_image, ivec2(gl_FragCoord.xy), g_color);" NL " discard;" NL "}"; const char* const glsl_draw_fs = "#version 420 core" NL "in vec2 vs_texcoord;" NL "layout(location = 0) out vec4 o_color;" NL "uniform sampler2D g_sampler;" NL "void main() {" NL " o_color = texture(g_sampler, vs_texcoord);" NL "}"; m_store_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_store_fs); m_draw_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_draw_fs); int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 1, 1, 16, 16); glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL); glBindTexture(GL_TEXTURE_2D, 0); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glViewport(0, 0, width, height); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glUseProgram(m_store_program); glUniform4f(glGetUniformLocation(m_store_program, "g_color"), 1.0f, 0.0f, 0.0f, 1.0f); glBindVertexArray(m_vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUniform4f(glGetUniformLocation(m_store_program, "g_color"), 0.0f, 1.0f, 0.0f, 1.0f); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, m_texture); glUseProgram(m_draw_program); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (!ValidateReadBuffer(0, 0, width, height, vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteTextures(1, &m_texture); glDeleteProgram(m_store_program); glDeleteProgram(m_draw_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.1.7 AdvancedSyncBufferUpdate //----------------------------------------------------------------------------- class AdvancedSyncBufferUpdate : public ShaderImageLoadStoreBase { GLuint m_buffer; GLuint m_buffer_tex; GLuint m_store_program; GLuint m_attribless_vao; virtual long Setup() { m_buffer = 0; m_buffer_tex = 0; m_store_program = 0; m_attribless_vao = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; const char* const glsl_store_vs = "#version 420 core" NL "writeonly uniform imageBuffer g_output_data;" NL "void main() {" NL " imageStore(g_output_data, gl_VertexID, vec4(gl_VertexID));" NL "}"; m_store_program = BuildProgram(glsl_store_vs, NULL, NULL, NULL, NULL); glGenVertexArrays(1, &m_attribless_vao); glBindVertexArray(m_attribless_vao); glGenBuffers(1, &m_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec4) * 1000, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_buffer); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glEnable(GL_RASTERIZER_DISCARD); glUseProgram(m_store_program); glDrawArrays(GL_POINTS, 0, 1000); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); vec4* ptr = reinterpret_cast(glMapBufferRange(GL_TEXTURE_BUFFER, 0, 1000 * sizeof(vec4), GL_MAP_READ_BIT)); for (int i = 0; i < 1000; ++i) { if (!IsEqual(ptr[i], vec4(static_cast(i)))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Bad buffer value found at index " << i << tcu::TestLog::EndMessage; return ERROR; } } return NO_ERROR; } virtual long Cleanup() { glDisable(GL_RASTERIZER_DISCARD); glUseProgram(0); glDeleteBuffers(1, &m_buffer); glDeleteTextures(1, &m_buffer_tex); glDeleteProgram(m_store_program); glDeleteVertexArrays(1, &m_attribless_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.2.1 AdvancedAllStagesOneImage //----------------------------------------------------------------------------- class AdvancedAllStagesOneImage : public ShaderImageLoadStoreBase { GLuint m_program; GLuint m_vao; GLuint m_vbo; GLuint m_ebo; GLuint m_buffer; GLuint m_buffer_tex; GLuint m_texture; virtual long Setup() { m_program = 0; m_vao = 0; m_vbo = 0; m_ebo = 0; m_buffer = 0; m_buffer_tex = 0; m_texture = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInGeomStages(2)) return NOT_SUPPORTED; const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(r32i) coherent uniform iimageBuffer g_buffer;" NL "layout(r32ui) coherent uniform uimage2D g_image;" NL "void main() {" NL " gl_Position = i_position + vec4(1.0, 1.0, 0.0, 0.0);" NL " imageAtomicAdd(g_buffer, 0, 1);" NL " imageAtomicAdd(g_image, ivec2(0, 0), 2u);" NL "}"; const char* const glsl_tcs = "#version 420 core" NL "layout(vertices = 1) out;" NL "layout(r32i) coherent uniform iimageBuffer g_buffer;" NL "layout(r32ui) coherent uniform uimage2D g_image;" NL "void main() {" NL " gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position;" NL " gl_TessLevelInner[0] = 1.0;" NL " gl_TessLevelInner[1] = 1.0;" NL " gl_TessLevelOuter[0] = 1.0;" NL " gl_TessLevelOuter[1] = 1.0;" NL " gl_TessLevelOuter[2] = 1.0;" NL " gl_TessLevelOuter[3] = 1.0;" NL " imageAtomicAdd(g_buffer, 0, 1);" NL " imageAtomicAdd(g_image, ivec2(0, 0), 2u);" NL "}"; const char* const glsl_tes = "#version 420 core" NL "layout(triangles, point_mode) in;" NL "layout(r32i) coherent uniform iimageBuffer g_buffer;" NL "layout(r32ui) coherent uniform uimage2D g_image;" NL "void main() {" NL " imageAtomicAdd(g_buffer, 0, 1);" NL " imageAtomicAdd(g_image, ivec2(0, 0), 2u);" NL " gl_Position = gl_in[0].gl_Position;" NL "}"; const char* const glsl_gs = "#version 420 core" NL "layout(points) in;" NL "layout(points, max_vertices = 1) out;" NL "layout(r32i) coherent uniform iimageBuffer g_buffer;" NL "layout(r32ui) coherent uniform uimage2D g_image;" NL "void main() {" NL " imageAtomicAdd(g_buffer, 0, 1);" NL " imageAtomicAdd(g_image, ivec2(0, 0), 2u);" NL " gl_Position = gl_in[0].gl_Position;" NL " EmitVertex();" NL "}"; const char* const glsl_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(r32i) coherent uniform iimageBuffer g_buffer;" NL "layout(r32ui) coherent uniform uimage2D g_image;" NL "void main() {" NL " imageAtomicAdd(g_buffer, 0, 1);" NL " imageAtomicAdd(g_image, ivec2(0, 0), 2u);" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL "}"; m_program = BuildProgram(glsl_vs, glsl_tcs, glsl_tes, glsl_gs, glsl_fs); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_buffer"), 0); glUniform1i(glGetUniformLocation(m_program, "g_image"), 1); CreateFullViewportQuad(&m_vao, &m_vbo, &m_ebo); GLint i32 = 0; glGenBuffers(1, &m_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, 4, &i32, GL_STATIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, m_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); GLuint ui32 = 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_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, 1, 1, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, &ui32); glBindTexture(GL_TEXTURE_2D, 0); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32I); glBindImageTexture(1, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); glBindVertexArray(m_vao); glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawElementsInstancedBaseVertex(GL_PATCHES, 1, GL_UNSIGNED_SHORT, 0, 1, 0); glDrawElementsInstanced(GL_PATCHES, 1, GL_UNSIGNED_SHORT, 0, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glGetBufferSubData(GL_TEXTURE_BUFFER, 0, 4, &i32); if (i32 < 20 || i32 > 50) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Buffer value is " << i32 << " should be in the range [20;50]" << tcu::TestLog::EndMessage; return ERROR; } glBindTexture(GL_TEXTURE_2D, m_texture); glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, &ui32); if (ui32 < 40 || ui32 != static_cast(2 * i32)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Texture value is " << ui32 << " should be " << (2 * i32) << tcu::TestLog::EndMessage; return ERROR; } return NO_ERROR; } virtual long Cleanup() { glPatchParameteri(GL_PATCH_VERTICES, 3); glUseProgram(0); glDeleteBuffers(1, &m_buffer); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); glDeleteTextures(1, &m_buffer_tex); glDeleteTextures(1, &m_texture); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.3.1 AdvancedMemoryDependentInvocation //----------------------------------------------------------------------------- class AdvancedMemoryDependentInvocation : public ShaderImageLoadStoreBase { GLuint m_buffer; GLuint m_buffer_tex; GLuint m_texture; GLuint m_program; GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_buffer = 0; m_buffer_tex = 0; m_texture = 0; m_program = 0; m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(2)) return NOT_SUPPORTED; const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(location = 1) in vec4 i_color;" NL "out vec4 vs_color;" NL "layout(rgba32f) coherent uniform imageBuffer g_buffer;" NL "layout(rgba32f) coherent uniform image2D g_image;" NL "void main() {" NL " gl_Position = i_position;" NL " vs_color = i_color;" NL " imageStore(g_buffer, 0, vec4(1.0));" NL " imageStore(g_image, ivec2(0, 0), vec4(2.0));" NL " memoryBarrier();" NL "}"; const char* const glsl_fs = "#version 420 core" NL "in vec4 vs_color;" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) coherent uniform imageBuffer g_buffer;" NL "layout(rgba32f) coherent uniform image2D g_image;" NL "void main() {" NL " o_color = vs_color;" NL " if (imageLoad(g_buffer, 0) != vec4(1.0)) o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image, ivec2(0, 0)) != vec4(2.0)) o_color = vec4(1.0, 0.0, 0.0, 0.2);" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_buffer"), 0); glUniform1i(glGetUniformLocation(m_program, "g_image"), 1); vec4 zero(0); glGenBuffers(1, &m_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec4), &zero, GL_STATIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, &zero); glBindTexture(GL_TEXTURE_2D, 0); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(m_vao); glDrawArraysInstancedBaseInstance(GL_TRIANGLE_STRIP, 0, 4, 1, 0); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_buffer); glDeleteTextures(1, &m_texture); glDeleteTextures(1, &m_buffer_tex); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.3.2 AdvancedMemoryOrder //----------------------------------------------------------------------------- class AdvancedMemoryOrder : public ShaderImageLoadStoreBase { GLuint m_buffer; GLuint m_buffer_tex; GLuint m_texture; GLuint m_program; GLuint m_vao; GLuint m_vbo; virtual long Setup() { m_buffer = 0; m_buffer_tex = 0; m_texture = 0; m_program = 0; m_vao = 0; m_vbo = 0; return NO_ERROR; } virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "out vec4 vs_color;" NL "layout(rgba32f) coherent uniform imageBuffer g_buffer;" NL "void main() {" NL " gl_Position = i_position;" NL " vs_color = vec4(0, 1, 0, 1);" NL " imageStore(g_buffer, gl_VertexID, vec4(1.0));" NL " imageStore(g_buffer, gl_VertexID, vec4(2.0));" NL " imageStore(g_buffer, gl_VertexID, vec4(3.0));" NL " if (imageLoad(g_buffer, gl_VertexID) != vec4(3.0)) vs_color = vec4(1, 0, 0, 1);" NL "}"; const char* const glsl_fs = "#version 420 core" NL "in vec4 vs_color;" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) uniform image2D g_image;" NL "void main() {" NL " o_color = vs_color;" NL " ivec2 coord = ivec2(gl_FragCoord);" NL " for (int i = 0; i < 3; ++i) {" NL " imageStore(g_image, coord, vec4(i));" NL " vec4 v = imageLoad(g_image, coord);" NL " if (v != vec4(i)) {" NL " o_color = vec4(v.xyz, 0.0);" NL " break;" NL " }" NL " }" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_buffer"), 0); glUniform1i(glGetUniformLocation(m_program, "g_image"), 1); glGenBuffers(1, &m_buffer); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec4) * 4, NULL, GL_STATIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); glGenTextures(1, &m_buffer_tex); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 1, 2, 16, 16); std::vector data(width * height); glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, &data[0]); glBindTexture(GL_TEXTURE_2D, 0); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glBindVertexArray(m_vao); glDrawArraysInstancedBaseInstance(GL_TRIANGLE_STRIP, 0, 4, 1, 0); if (!ValidateReadBuffer(0, 0, width, height, vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_buffer); glDeleteTextures(1, &m_texture); glDeleteTextures(1, &m_buffer_tex); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.4.1 AdvancedSSOSimple //----------------------------------------------------------------------------- class AdvancedSSOSimple : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_pipeline[2]; GLuint m_vsp, m_fsp0, m_fsp1; GLuint m_vao, m_vbo; virtual long Setup() { glGenTextures(1, &m_texture); glGenProgramPipelines(2, m_pipeline); CreateFullViewportQuad(&m_vao, &m_vbo, NULL); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "out gl_PerVertex {" NL " vec4 gl_Position;" NL "};" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_fs0 = "#version 420 core" NL "layout(rgba32f) uniform image2D g_image[4];" NL "void main() {" NL " for (int i = 0; i < g_image.length(); ++i) {" NL " imageStore(g_image[i], ivec2(gl_FragCoord), vec4(1.0));" NL " }" NL " discard;" NL "}"; const char* const glsl_fs1 = "#version 420 core" NL "writeonly uniform image2D g_image[4];" NL "void main() {" NL " for (int i = 0; i < g_image.length(); ++i) {" NL " imageStore(g_image[i], ivec2(gl_FragCoord), vec4(2.0));" NL " }" NL " discard;" NL "}"; m_vsp = BuildShaderProgram(GL_VERTEX_SHADER, glsl_vs); m_fsp0 = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_fs0); m_fsp1 = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_fs1); return NO_ERROR; } virtual long Run() { glProgramUniform1i(m_fsp0, glGetUniformLocation(m_fsp0, "g_image[0]"), 0); glProgramUniform1i(m_fsp0, glGetUniformLocation(m_fsp0, "g_image[1]"), 2); glProgramUniform1i(m_fsp0, glGetUniformLocation(m_fsp0, "g_image[2]"), 4); glProgramUniform1i(m_fsp0, glGetUniformLocation(m_fsp0, "g_image[3]"), 6); glProgramUniform1i(m_fsp1, glGetUniformLocation(m_fsp1, "g_image[0]"), 1); glProgramUniform1i(m_fsp1, glGetUniformLocation(m_fsp1, "g_image[1]"), 3); glProgramUniform1i(m_fsp1, glGetUniformLocation(m_fsp1, "g_image[2]"), 5); glProgramUniform1i(m_fsp1, glGetUniformLocation(m_fsp1, "g_image[3]"), 7); glUseProgramStages(m_pipeline[0], GL_VERTEX_SHADER_BIT, m_vsp); glUseProgramStages(m_pipeline[0], GL_FRAGMENT_SHADER_BIT, m_fsp0); glUseProgramStages(m_pipeline[1], GL_VERTEX_SHADER_BIT, m_vsp); glUseProgramStages(m_pipeline[1], GL_FRAGMENT_SHADER_BIT, m_fsp1); int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 8, 8, 16, 16); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA32F, width, height, 8, 0, GL_RGBA, GL_FLOAT, NULL); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture, 0, GL_FALSE, 1, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(2, m_texture, 0, GL_FALSE, 2, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(3, m_texture, 0, GL_FALSE, 3, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(4, m_texture, 0, GL_FALSE, 4, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(5, m_texture, 0, GL_FALSE, 5, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(6, m_texture, 0, GL_FALSE, 6, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(7, m_texture, 0, GL_FALSE, 7, GL_READ_WRITE, GL_RGBA32F); glBindVertexArray(m_vao); glViewport(0, 0, width, height); glBindProgramPipeline(m_pipeline[0]); glDrawArraysInstancedBaseInstance(GL_TRIANGLE_STRIP, 0, 4, 1, 0); glBindProgramPipeline(m_pipeline[1]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); std::vector data(width * height * 8); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_FLOAT, &data[0]); for (int layer = 0; layer < 8; ++layer) { for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { const vec4 c = data[layer * width * height + h * width + w]; if (layer % 2) { if (!IsEqual(c, vec4(2.0f))) { return ERROR; } } else { if (!IsEqual(c, vec4(1.0f))) { return ERROR; } } } } } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteBuffers(1, &m_vbo); glDeleteTextures(1, &m_texture); glDeleteProgram(m_vsp); glDeleteProgram(m_fsp0); glDeleteProgram(m_fsp1); glDeleteVertexArrays(1, &m_vao); glDeleteProgramPipelines(2, m_pipeline); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.4.2 AdvancedSSOAtomicCounters //----------------------------------------------------------------------------- class AdvancedSSOAtomicCounters : public ShaderImageLoadStoreBase { GLuint m_buffer, m_buffer_tex; GLuint m_counter_buffer; GLuint m_transform_buffer; GLuint m_pipeline; GLuint m_vao, m_vbo; GLuint m_vsp, m_fsp; virtual long Setup() { m_vao = 0; m_vbo = 0; m_vsp = 0; m_fsp = 0; glGenBuffers(1, &m_buffer); glGenTextures(1, &m_buffer_tex); glGenBuffers(1, &m_counter_buffer); glGenBuffers(1, &m_transform_buffer); glGenProgramPipelines(1, &m_pipeline); return NO_ERROR; } virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; CreateFullViewportQuad(&m_vao, &m_vbo, NULL); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(location = 1) in vec4 i_color;" NL "layout(location = 3) out vec4 o_color;" NL "out gl_PerVertex {" NL " vec4 gl_Position;" NL "};" NL "layout(std140) uniform Transform {" NL " mat4 mvp;" NL "} g_transform;" NL "writeonly uniform imageBuffer g_buffer;" NL "layout(binding = 0, offset = 0) uniform atomic_uint g_counter;" NL "void main() {" NL " gl_Position = g_transform.mvp * i_position;" NL " o_color = i_color;" NL " const uint index = atomicCounterIncrement(g_counter);" NL " imageStore(g_buffer, int(index), gl_Position);" NL "}"; const char* const glsl_fs = "#version 420 core" NL "layout(location = 3) in vec4 i_color;" NL "layout(location = 0) out vec4 o_color;" NL "void main() {" NL " o_color = i_color;" NL "}"; m_vsp = BuildShaderProgram(GL_VERTEX_SHADER, glsl_vs); m_fsp = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_fs); glUseProgramStages(m_pipeline, GL_VERTEX_SHADER_BIT, m_vsp); glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, m_fsp); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(vec4) * 4, NULL, GL_STATIC_DRAW); glBindTexture(GL_TEXTURE_BUFFER, m_buffer_tex); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_buffer); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_counter_buffer); vec4 zero(0); glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), &zero, GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, m_transform_buffer); mat4 identity(1); glBufferData(GL_UNIFORM_BUFFER, sizeof(mat4), &identity, GL_STATIC_DRAW); glClear(GL_COLOR_BUFFER_BIT); glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_counter_buffer); glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_transform_buffer); glBindImageTexture(0, m_buffer_tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindVertexArray(m_vao); glBindProgramPipeline(m_pipeline); glDrawArraysInstancedBaseInstance(GL_TRIANGLE_STRIP, 0, 4, 1, 0); if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) { return ERROR; } std::vector data(4); glBindBuffer(GL_TEXTURE_BUFFER, m_buffer); glGetBufferSubData(GL_TEXTURE_BUFFER, 0, sizeof(vec4) * 4, &data[0]); for (int i = 0; i < 4; ++i) { if (!IsEqual(data[i], vec4(-1.0f, -1.0f, 0.0f, 1.0f)) && !IsEqual(data[i], vec4(1.0f, -1.0f, 0.0f, 1.0f)) && !IsEqual(data[i], vec4(-1.0f, 1.0f, 0.0f, 1.0f)) && !IsEqual(data[i], vec4(1.0f, 1.0f, 0.0f, 1.0f))) { return ERROR; } } return NO_ERROR; } virtual long Cleanup() { glDeleteBuffers(1, &m_buffer); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_counter_buffer); glDeleteBuffers(1, &m_transform_buffer); glDeleteTextures(1, &m_buffer_tex); glDeleteProgram(m_vsp); glDeleteProgram(m_fsp); glDeleteVertexArrays(1, &m_vao); glDeleteProgramPipelines(1, &m_pipeline); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.4.3 AdvancedSSOSubroutine //----------------------------------------------------------------------------- class AdvancedSSOSubroutine : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_attribless_vao; GLuint m_program; GLint m_draw_buffer; virtual long Setup() { glGenTextures(1, &m_texture); glGenVertexArrays(1, &m_attribless_vao); const char* const glsl_vs = "#version 420 core" NL "const int kSize = 3;" NL "const vec2 g_triangle[kSize] = vec2[3](vec2(-1, -1), vec2(3, -1), vec2(-1, 3));" NL "void main() {" NL " gl_Position = vec4(g_triangle[gl_VertexID], 0, 1);" NL "}"; const char* const glsl_fs = "#version 420 core" NL "writeonly uniform image2DArray g_image0;" NL "writeonly uniform image2DArray g_image1;" NL "subroutine void Brush(ivec2 coord);" NL "subroutine uniform Brush g_brush;" NL "subroutine(Brush) void Brush0(ivec2 coord) {" NL " imageStore(g_image0, ivec3(coord, 0), vec4(1.0, 0.0, 0.0, 1.0));" NL " imageStore(g_image0, ivec3(coord, 1), vec4(0.0, 1.0, 0.0, 1.0));" NL " imageStore(g_image0, ivec3(coord, 2), vec4(0.0, 0.0, 1.0, 1.0));" NL "}" NL "subroutine(Brush) void Brush1(ivec2 coord) {" NL " imageStore(g_image1, ivec3(coord, 0), vec4(0.0, 1.0, 0.0, 1.0));" NL " imageStore(g_image1, ivec3(coord, 1), vec4(0.0, 0.0, 1.0, 1.0));" NL " imageStore(g_image1, ivec3(coord, 2), vec4(1.0, 0.0, 0.0, 1.0));" NL "}" NL "void main() {" NL " g_brush(ivec2(gl_FragCoord));" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); return NO_ERROR; } virtual long Run() { int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 3, 3, 16, 16); glProgramUniform1i(m_program, glGetUniformLocation(m_program, "g_image0"), 1); glProgramUniform1i(m_program, glGetUniformLocation(m_program, "g_image1"), 1); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA32F, width, height, 3, 0, GL_RGBA, GL_FLOAT, NULL); glGetIntegerv(GL_DRAW_BUFFER, &m_draw_buffer); glDrawBuffer(GL_NONE); glBindImageTexture(1, m_texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F); glUseProgram(m_program); glBindVertexArray(m_attribless_vao); const GLuint indices[2] = { glGetSubroutineIndex(m_program, GL_FRAGMENT_SHADER, "Brush0"), glGetSubroutineIndex(m_program, GL_FRAGMENT_SHADER, "Brush1") }; glViewport(0, 0, width, height); glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &indices[0]); glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 0, 3, 1, 0); std::vector data(width * height * 3); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_FLOAT, &data[0]); for (int layer = 0; layer < 3; ++layer) { for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { const vec4 c = data[layer * width * height + h * width + w]; if (layer == 0 && !IsEqual(c, vec4(1.0f, 0.0f, 0.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (1 0 0 1)" << tcu::TestLog::EndMessage; return ERROR; } else if (layer == 1 && !IsEqual(c, vec4(0.0f, 1.0f, 0.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (0 1 0 1)" << tcu::TestLog::EndMessage; return ERROR; } else if (layer == 2 && !IsEqual(c, vec4(0.0f, 0.0f, 1.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (0 0 1 1)" << tcu::TestLog::EndMessage; return ERROR; } } } } glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &indices[1]); glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 0, 3, 1, 0); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_FLOAT, &data[0]); for (int layer = 0; layer < 3; ++layer) { for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { const vec4 c = data[layer * width * height + h * width + w]; if (layer == 0 && !IsEqual(c, vec4(0.0f, 1.0f, 0.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (0 1 0 1)" << tcu::TestLog::EndMessage; return ERROR; } else if (layer == 1 && !IsEqual(c, vec4(0.0f, 0.0f, 1.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (0 0 1 1)" << tcu::TestLog::EndMessage; return ERROR; } else if (layer == 2 && !IsEqual(c, vec4(1.0f, 0.0f, 0.0f, 1.0f))) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (1 0 0 1)" << tcu::TestLog::EndMessage; return ERROR; } } } } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDrawBuffer(m_draw_buffer); glDeleteTextures(1, &m_texture); glUseProgram(0); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_attribless_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.4.4 AdvancedSSOPerSample //----------------------------------------------------------------------------- class AdvancedSSOPerSample : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_pipeline; GLuint m_vao, m_vbo, m_ebo; GLuint m_vsp, m_store_fsp, m_load_fsp; virtual long Setup() { m_vao = 0; m_vbo = 0; m_ebo = 0; m_vsp = 0; m_store_fsp = 0; m_load_fsp = 0; glGenTextures(1, &m_texture); glGenProgramPipelines(1, &m_pipeline); return NO_ERROR; } virtual long Run() { if (!SupportedSamples(4)) return NOT_SUPPORTED; CreateFullViewportQuad(&m_vao, &m_vbo, &m_ebo); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "out gl_PerVertex {" NL " vec4 gl_Position;" NL "};" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_store_fs = "#version 420 core" NL "layout(rgba32f) writeonly uniform image2DMS g_image;" NL "void main() {" NL " imageStore(g_image, ivec2(gl_FragCoord), gl_SampleID, vec4(gl_SampleID+1));" NL "}"; const char* const glsl_load_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) readonly uniform image2DMS g_image;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " if (imageLoad(g_image, ivec2(gl_FragCoord), gl_SampleID) != " "vec4(gl_SampleID+1)) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; m_vsp = BuildShaderProgram(GL_VERTEX_SHADER, glsl_vs); m_store_fsp = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_store_fs); m_load_fsp = BuildShaderProgram(GL_FRAGMENT_SHADER, glsl_load_fs); int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 1, 1, /* bpp*samples */ 16 * 4, 16); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_texture); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA32F, width, height, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glUseProgramStages(m_pipeline, GL_VERTEX_SHADER_BIT, m_vsp); glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, m_store_fsp); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glBindVertexArray(m_vao); glBindProgramPipeline(m_pipeline); glDrawElementsInstancedBaseInstance(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgramStages(m_pipeline, GL_FRAGMENT_SHADER_BIT, m_load_fsp); glDrawElementsInstancedBaseInstance(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); if (!ValidateReadBuffer(0, 0, width, height, vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); glDeleteTextures(1, &m_texture); glDeleteProgram(m_vsp); glDeleteProgram(m_store_fsp); glDeleteProgram(m_load_fsp); glDeleteVertexArrays(1, &m_vao); glDeleteProgramPipelines(1, &m_pipeline); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.5 AdvancedCopyImage //----------------------------------------------------------------------------- class AdvancedCopyImage : public ShaderImageLoadStoreBase { GLuint m_texture[2]; GLuint m_program; GLuint m_vao, m_vbo, m_ebo; virtual long Setup() { glGenTextures(2, m_texture); CreateFullViewportQuad(&m_vao, &m_vbo, &m_ebo); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_fs = "#version 420 core" NL "layout(rgba32f) readonly uniform image2D g_input_image;" NL "layout(rgba32f) writeonly uniform image2D g_output_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord);" NL " imageStore(g_output_image, coord, imageLoad(g_input_image, coord));" NL " discard;" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); return NO_ERROR; } virtual long Run() { int width = getWindowWidth(); int height = getWindowHeight(); scaleDimensionsToMemory(width, height, 2, 2, 16, 16); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_input_image"), 0); glUniform1i(glGetUniformLocation(m_program, "g_output_image"), 1); std::vector data(width * height, vec4(7.0f)); glBindTexture(GL_TEXTURE_2D, m_texture[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, &data[0]); glBindTexture(GL_TEXTURE_2D, m_texture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL); glBindTexture(GL_TEXTURE_2D, 0); glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glBindVertexArray(m_vao); glDrawElementsInstancedBaseInstance(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); std::vector rdata(width * height); glBindTexture(GL_TEXTURE_2D, m_texture[1]); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &rdata[0]); for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { if (!IsEqual(rdata[h * width + w], vec4(7.0f))) { return ERROR; } } } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); glDeleteTextures(2, m_texture); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.6 AdvancedAllMips //----------------------------------------------------------------------------- class AdvancedAllMips : public ShaderImageLoadStoreBase { GLuint m_texture; GLuint m_store_program, m_load_program; GLuint m_vao, m_vbo, m_ebo; virtual long Setup() { glGenTextures(1, &m_texture); CreateFullViewportQuad(&m_vao, &m_vbo, &m_ebo); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_store_fs = "#version 420 core" NL "layout(rgba32f) uniform image2D g_image[6];" NL "void main() {" NL " for (int i = 0; i < 6; ++i) {" NL " imageStore(g_image[i], ivec2(gl_FragCoord), vec4(i));" NL " }" NL " discard;" NL "}"; const char* const glsl_load_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) uniform image2D g_image[6];" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " for (int i = 0; i < 6; ++i) {" NL " const ivec2 coord = ivec2(gl_FragCoord);" NL " const vec4 c = imageLoad(g_image[i], coord);" NL " if (c != vec4(i) && c != vec4(0.0)) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " }" NL "}"; m_store_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_store_fs); m_load_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_load_fs); return NO_ERROR; } virtual long Run() { glUseProgram(m_store_program); glUniform1i(glGetUniformLocation(m_store_program, "g_image[0]"), 0); glUniform1i(glGetUniformLocation(m_store_program, "g_image[1]"), 1); glUniform1i(glGetUniformLocation(m_store_program, "g_image[2]"), 2); glUniform1i(glGetUniformLocation(m_store_program, "g_image[3]"), 3); glUniform1i(glGetUniformLocation(m_store_program, "g_image[4]"), 4); glUniform1i(glGetUniformLocation(m_store_program, "g_image[5]"), 5); glUseProgram(0); glUseProgram(m_load_program); glUniform1i(glGetUniformLocation(m_load_program, "g_image[0]"), 0); glUniform1i(glGetUniformLocation(m_load_program, "g_image[1]"), 1); glUniform1i(glGetUniformLocation(m_load_program, "g_image[2]"), 2); glUniform1i(glGetUniformLocation(m_load_program, "g_image[3]"), 3); glUniform1i(glGetUniformLocation(m_load_program, "g_image[4]"), 4); glUniform1i(glGetUniformLocation(m_load_program, "g_image[5]"), 5); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, m_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 32, 32, 0, GL_RGBA, GL_FLOAT, NULL); glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 16, 16, 0, GL_RGBA, GL_FLOAT, NULL); glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA32F, 8, 8, 0, GL_RGBA, GL_FLOAT, NULL); glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA32F, 4, 4, 0, GL_RGBA, GL_FLOAT, NULL); glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA32F, 2, 2, 0, GL_RGBA, GL_FLOAT, NULL); glTexImage2D(GL_TEXTURE_2D, 5, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, NULL); glBindTexture(GL_TEXTURE_2D, 0); glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(1, m_texture, 1, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(2, m_texture, 2, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(3, m_texture, 3, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(4, m_texture, 4, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glBindImageTexture(5, m_texture, 5, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); glViewport(0, 0, 32, 32); glBindVertexArray(m_vao); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_store_program); glDrawElementsInstancedBaseInstance(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgram(m_load_program); glDrawElementsInstancedBaseVertex(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); if (!ValidateReadBuffer(0, 0, 32, 32, vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glViewport(0, 0, getWindowWidth(), getWindowHeight()); glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); glDeleteTextures(1, &m_texture); glDeleteProgram(m_store_program); glDeleteProgram(m_load_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 2.7 AdvancedCast //----------------------------------------------------------------------------- class AdvancedCast : public ShaderImageLoadStoreBase { GLuint m_texture[2]; GLuint m_program; GLuint m_vao, m_vbo, m_ebo; virtual long Setup() { glGenTextures(2, m_texture); CreateFullViewportQuad(&m_vao, &m_vbo, &m_ebo); const char* const glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* const glsl_fs = "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(r32i) coherent uniform iimage2D g_image0;" NL "layout(r32ui) coherent uniform uimage2D g_image1;" NL "void main() {" NL " o_color = vec4(0.0, 1.0, 0.0, 1.0);" NL " ivec2 coord = ivec2(gl_FragCoord);" NL " if (imageAtomicAdd(g_image0, coord, 2) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image0, coord, -1) != 2) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image1, coord, 1) != 0) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL " if (imageAtomicAdd(g_image1, coord, 2) != 1) o_color = vec4(1.0, 0.0, 0.0, 1.0);" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); return NO_ERROR; } virtual long Run() { glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_image0"), 0); glUniform1i(glGetUniformLocation(m_program, "g_image1"), 1); { std::vector data(getWindowWidth() * getWindowHeight() * 4); glBindTexture(GL_TEXTURE_2D, m_texture[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); glBindTexture(GL_TEXTURE_2D, m_texture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); glBindTexture(GL_TEXTURE_2D, 0); } glBindImageTexture(0, m_texture[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32I); glBindImageTexture(1, m_texture[1], 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(m_vao); glDrawElementsInstancedBaseInstance(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0, 1, 0); std::vector data(getWindowWidth() * getWindowHeight() * 4); glBindTexture(GL_TEXTURE_2D, m_texture[0]); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); for (int h = 0; h < getWindowHeight(); ++h) { for (int w = 0; w < getWindowWidth(); ++w) { const GLubyte c[4] = { data[h * (getWindowWidth() * 4) + w * 4 + 0], data[h * (getWindowWidth() * 4) + w * 4 + 1], data[h * (getWindowWidth() * 4) + w * 4 + 2], data[h * (getWindowWidth() * 4) + w * 4 + 3], }; if (c[0] != 1 || c[1] != 0 || c[2] != 0 || c[3] != 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (1 0 0 0)" << tcu::TestLog::EndMessage; return ERROR; } } } glBindTexture(GL_TEXTURE_2D, m_texture[1]); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); for (int h = 0; h < getWindowHeight(); ++h) { for (int w = 0; w < getWindowWidth(); ++w) { const GLubyte c[4] = { data[h * (getWindowWidth() * 4) + w * 4 + 0], data[h * (getWindowWidth() * 4) + w * 4 + 1], data[h * (getWindowWidth() * 4) + w * 4 + 2], data[h * (getWindowWidth() * 4) + w * 4 + 3], }; if (c[0] != 3 || c[1] != 0 || c[2] != 0 || c[3] != 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Color is (" << c[0] << " " << c[1] << c[2] << " " << c[3] << ") should be (3 0 0 0)" << tcu::TestLog::EndMessage; return ERROR; } } } if (!ValidateReadBuffer(0, 0, getWindowWidth(), getWindowHeight(), vec4(0, 1, 0, 1))) { return ERROR; } return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); glDeleteTextures(2, m_texture); glDeleteProgram(m_program); glDeleteVertexArrays(1, &m_vao); return NO_ERROR; } }; /** Test "imageLoad() and imageStore() for single-byte data alignment" description follows. * * Steps: * - create two textures: "source" and "destination". Fill "source" * texture with unique values. Fill "destination" texture with zeros, * - prepare a program object that will read texel from "source" image at given * coordinates and write its value to "destination" image at same * coordinates, * - bind "source" and "destination" textures as "source" and "destination" * image uniforms, * - render "full screen" quad (left bottom corner at -1,-1 and right top * corner at 1,1), * - verify that texel values in "destination" texture match those in * "source" texture (use glGetTexImage). * * Test with 2D R8UI textures with following dimensions: * - 16x16, * - 16x17, * - 17x16, * - 17x17, * - 16x18, * - 18x16, * - 18x18, * - 19x16, * - 16x19, * - 19x19. * * Note that default data alignment should cause problems with packing/ * /unpacking. Therefore GL_PACK_ALIGNMENT and GL_UNPACK_ALIGNMENT parameters * of pixel storage mode have to be changed to one byte alignment. * * Program should consist of vertex and fragment shader. Vertex shader should * pass vertex position through. Fragment shader should do imageLoad() and * imageStore() operations at coordinates gl_FragCoord. **/ class ImageLoadStoreDataAlignmentTest : public ShaderImageLoadStoreBase { private: /* Structures */ struct TextureDimensions { GLuint m_width; GLuint m_height; TextureDimensions(GLuint width, GLuint height) : m_width(width), m_height(height) { } }; /* Typedefs */ typedef std::deque TextureDimensionsList; /* Fields */ GLuint m_destination_texture_id; GLuint m_program_id; TextureDimensionsList m_texture_dimensions; GLuint m_source_texture_id; GLuint m_vertex_array_object_id; GLuint m_vertex_buffer_id; public: /* Constructor */ ImageLoadStoreDataAlignmentTest() : m_destination_texture_id(0) , m_program_id(0) , m_source_texture_id(0) , m_vertex_array_object_id(0) , m_vertex_buffer_id(0) { /* Nothing to be done here */ } /* Methods inherited from SubcaseBase */ virtual long Setup() { /* Shaders code */ const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in vec4 vs_in_position;\n" "\n" "void main()\n" "{\n" " gl_Position = vs_in_position;\n" "}\n"; const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(r8ui) writeonly uniform uimage2D u_destination_image;\n" "layout(r8ui) readonly uniform uimage2D u_source_image;\n" "\n" "void main()\n" "{\n" " uvec4 loaded_color = imageLoad (u_source_image, ivec2(gl_FragCoord));\n" " imageStore(u_destination_image, ivec2(gl_FragCoord), loaded_color);\n" "\n" " discard;\n" "}\n"; /* Vertex postions for "full screen" quad, made with triangle strip */ static const GLfloat m_vertex_buffer_data[] = { -1.0f, -1.0f, 0.0f, 1.0f, /* left bottom */ -1.0f, 1.0f, 0.0f, 1.0f, /* left top */ 1.0f, -1.0f, 0.0f, 1.0f, /* right bottom */ 1.0f, 1.0f, 0.0f, 1.0f, /* right top */ }; /* Result of BuildProgram operation */ bool is_program_correct = true; /* BuildProgram set false when it fails, but it does not set true on success */ /* Add all tested texture dimensions */ m_texture_dimensions.push_back(TextureDimensions(16, 16)); m_texture_dimensions.push_back(TextureDimensions(16, 17)); m_texture_dimensions.push_back(TextureDimensions(17, 16)); m_texture_dimensions.push_back(TextureDimensions(17, 17)); m_texture_dimensions.push_back(TextureDimensions(16, 18)); m_texture_dimensions.push_back(TextureDimensions(18, 16)); m_texture_dimensions.push_back(TextureDimensions(18, 18)); m_texture_dimensions.push_back(TextureDimensions(16, 19)); m_texture_dimensions.push_back(TextureDimensions(19, 16)); m_texture_dimensions.push_back(TextureDimensions(19, 19)); /* Clean previous error */ glGetError(); /* Set single-byte data alignment */ glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GLU_EXPECT_NO_ERROR(glGetError(), "PixelStorei"); /* Prepare buffer with vertex positions of "full screen" quad" */ glGenBuffers(1, &m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenBuffers"); glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertex_buffer_data), m_vertex_buffer_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(glGetError(), "BufferData"); /* Generate vertex array object */ glGenVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenVertexArrays"); /* Prepare program object */ m_program_id = BuildProgram(vertex_shader_code, 0 /* src_tcs */, 0 /* src_tes */, 0 /*src_gs */, fragment_shader_code, &is_program_correct); if (false == is_program_correct) { return ERROR; } /* Done */ return NO_ERROR; } virtual long Cleanup() { /* Reset OpenGL state */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindVertexArray(0); glUseProgram(0); /* Delete program */ if (0 != m_program_id) { glDeleteProgram(m_program_id); m_program_id = 0; } /* Delete textures */ if (0 != m_destination_texture_id) { glDeleteTextures(1, &m_destination_texture_id); m_destination_texture_id = 0; } if (0 != m_source_texture_id) { glDeleteTextures(1, &m_source_texture_id); m_source_texture_id = 0; } /* Delete vertex array object */ if (0 != m_vertex_array_object_id) { glDeleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } /* Delete buffer */ if (0 != m_vertex_buffer_id) { glDeleteBuffers(1, &m_vertex_buffer_id); m_vertex_buffer_id = 0; } /* Done */ return NO_ERROR; } virtual long Run() { bool result = true; /* For each dimension */ for (TextureDimensionsList::const_iterator it = m_texture_dimensions.begin(); m_texture_dimensions.end() != it; ++it) { /* Prepare "source" and "destination" textures */ GLU_EXPECT_NO_ERROR(Create2DR8UIDestinationTexture(it->m_width, it->m_height, m_destination_texture_id), "Create2DR8UIDestinationTexture"); GLU_EXPECT_NO_ERROR(Create2DR8UISourceTexture(it->m_width, it->m_height, m_source_texture_id), "Create2DR8UISourceTexture"); /* Copy texture data with imageLoad() and imageStore() operations */ Copy2DR8UITexture(m_destination_texture_id, m_source_texture_id); /* Compare "source" and "destination" textures */ if (false == Compare2DR8UITextures(m_destination_texture_id, m_source_texture_id, it->m_width, it->m_height)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Copy with imageLoad and imageStore failed for textures: " << it->m_width << "x" << it->m_height << ". Source and destination textures are different" << tcu::TestLog::EndMessage; result = false; } /* Destroy "source" and "destination" textures */ glDeleteTextures(1, &m_destination_texture_id); glDeleteTextures(1, &m_source_texture_id); m_destination_texture_id = 0; m_source_texture_id = 0; } if (false == result) { return ERROR; } /* Done */ return NO_ERROR; } private: /* Private methods */ /** Binds a texture to user-specified image unit and updates relevant sampler uniform * * @param program_id Program object id * @param texture_id Texture id * @param image_unit Index of image unit * @param uniform_name Name of image uniform **/ void BindTextureToImage(GLuint program_id, GLuint texture_id, GLuint image_unit, const char* uniform_name) { /* Uniform location and invalid value */ static const GLint invalid_uniform_location = -1; GLint image_uniform_location = 0; /* Get uniform location */ image_uniform_location = glGetUniformLocation(program_id, uniform_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (invalid_uniform_location == image_uniform_location) { throw tcu::InternalError("A required uniform is considered inactive", uniform_name, __FILE__, __LINE__); } /* Bind texture to image unit */ glBindImageTexture(image_unit, texture_id, 0 /* level */, GL_FALSE, 0 /* layer */, GL_READ_WRITE, GL_R8UI); GLU_EXPECT_NO_ERROR(glGetError(), "BindImageTexture"); /* Set uniform to image unit */ glUniform1i(image_uniform_location, image_unit); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); } /** Compare two 2D R8UI textures * * @param left_texture_id Id of "left" texture object * @param right_texture_id Id of "right" texture object * @param width Width of the textures * @param height Height of the textures * * @return true when texture data is identical, false otherwise **/ bool Compare2DR8UITextures(GLuint left_texture_id, GLuint right_texture_id, GLuint width, GLuint height) { /* Size of textures */ const GLuint texture_data_size = width * height; /* Storage for texture data */ std::vector left_texture_data; std::vector right_texture_data; /* Alocate memory for texture data */ left_texture_data.resize(texture_data_size); right_texture_data.resize(texture_data_size); /* Get "left" texture data */ glBindTexture(GL_TEXTURE_2D, left_texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &left_texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); /* Get "right" texture data */ glBindTexture(GL_TEXTURE_2D, right_texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &right_texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); /* Compare texels */ return (0 == memcmp(&left_texture_data[0], &right_texture_data[0], texture_data_size)); } /** Copy contents of "source" texture to "destination" texture with imageLoad() and imageStore() operations * * @param destination_texture_id Id of "destination" texture object * @param source_texture_id Id of "source" texture object **/ void Copy2DR8UITexture(GLuint destination_texture_id, GLuint source_texture_id) { /* Uniform names */ static const char* const destination_image_uniform_name = "u_destination_image"; static const char* const source_image_uniform_name = "u_source_image"; /* Attribute name */ static const char* const position_attribute_name = "vs_in_position"; /* Attribute location and invalid value */ static const GLint invalid_attribute_location = -1; GLint position_attribute_location = 0; /* Set current program */ glUseProgram(m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "UseProgram"); /* Bind vertex array object */ glBindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindVertexArray"); /* Bind buffer with quad vertex positions */ glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); /* Set up position attribute */ position_attribute_location = glGetAttribLocation(m_program_id, position_attribute_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetAttribLocation"); if (invalid_attribute_location == position_attribute_location) { throw tcu::InternalError("Attribute location is not available", position_attribute_name, __FILE__, __LINE__); } glVertexAttribPointer(position_attribute_location, 4 /*size */, GL_FLOAT, GL_FALSE /* normalized */, 0 /* stride */, 0); GLU_EXPECT_NO_ERROR(glGetError(), "VertexAttribPointer"); glEnableVertexAttribArray(position_attribute_location); GLU_EXPECT_NO_ERROR(glGetError(), "EnableVertexAttribArray"); /* Set up textures as source and destination images */ BindTextureToImage(m_program_id, destination_texture_id, 0 /* image_unit */, destination_image_uniform_name); BindTextureToImage(m_program_id, source_texture_id, 1 /* image_unit */, source_image_uniform_name); /* Execute draw */ glDrawArrays(GL_TRIANGLE_STRIP, 0 /* first vertex */, 4 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); } /** Create 2D R8UI texture and fills it with zeros * * @param width Width of created texture * @param height Height of created texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum Create2DR8UIDestinationTexture(GLuint width, GLuint height, GLuint& out_texture_id) { /* Texture size */ const GLuint texture_size = width * height; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_size); /* Set all texels */ for (GLuint i = 0; i < texture_size; ++i) { texture_data[i] = 0; } /* Create texture */ return Create2DR8UITexture(width, height, texture_data, out_texture_id); } /** Create 2D R8UI texture and fills it with increasing values, starting from 0 * * @param width Width of created texture * @param height Height of created texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum Create2DR8UISourceTexture(GLuint width, GLuint height, GLuint& out_texture_id) { /* Texture size */ const GLuint texture_size = width * height; /* Value of texel */ GLubyte texel_value = 0; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_size); /* Set all texels */ for (GLuint i = 0; i < texture_size; ++i) { texture_data[i] = texel_value++; } /* Create texture */ return Create2DR8UITexture(width, height, texture_data, out_texture_id); } /** Create 2D R8UI texture and fills it with user-provided data * * @param width Width of created texture * @param height Height of created texture * @param texture_data Texture data * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum Create2DR8UITexture(GLuint width, GLuint height, const std::vector& texture_data, GLuint& out_texture_id) { GLenum err = 0; GLuint texture_id = 0; /* Generate texture */ glGenTextures(1, &texture_id); err = glGetError(); if (GL_NO_ERROR != err) { return err; } /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Allocate storage and fill texture */ glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_R8UI, width, height, 0 /* border */, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &texture_data[0]); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Make texture complete */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Set out_texture_id */ out_texture_id = texture_id; /* Done */ return GL_NO_ERROR; } }; /** Test "imageLoad() and imageStore() for non-layered image bindings" description follows. * * Steps: same as in test 1 (ImageLoadStoreDataAlignmentTest). * * Test non-layered image bindings (BindImageTexture : false) with: * | Type | Dimensions | * | 2D_ARRAY | 64x64x6 | * | 3D | 64x64x6 | * | CUBE_MAP | 64 | * | CUBE_MAP_ARRAY | 64x3 | * * Use RGBA8 format. All layers shall be tested. * * Program should consist of vertex and fragment shader. Vertex shader should * pass vertex position through. Fragment shader should do imageLoad() and * imageStore() operations at coordinates gl_FragCoord. Fragment shader should * use image2D as image type. **/ class ImageLoadStoreNonLayeredBindingTest : public ShaderImageLoadStoreBase { private: /* Structures */ struct TextureShapeDefinition { GLuint m_edge; GLuint m_n_elements; GLenum m_type; TextureShapeDefinition(GLuint edge, GLuint n_elements, GLenum type) : m_edge(edge), m_n_elements(n_elements), m_type(type) { } }; /* Typedefs */ typedef std::deque TextureShapeDefinitionList; /* Fields */ GLuint m_destination_texture_id; GLuint m_program_id; TextureShapeDefinitionList m_texture_shape_definitions; GLuint m_source_texture_id; GLuint m_vertex_array_object_id; GLuint m_vertex_buffer_id; public: /* Constructor */ ImageLoadStoreNonLayeredBindingTest() : m_destination_texture_id(0) , m_program_id(0) , m_source_texture_id(0) , m_vertex_array_object_id(0) , m_vertex_buffer_id(0) { /* Nothing to be done here */ } /* Methods inherited from SubcaseBase */ virtual long Setup() { /* Shaders code */ const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in vec4 vs_in_position;\n" "\n" "void main()\n" "{\n" " gl_Position = vs_in_position;\n" "}\n"; const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(rgba8) writeonly uniform image2D u_destination_image;\n" "layout(rgba8) readonly uniform image2D u_source_image;\n" "\n" "void main()\n" "{\n" " vec4 loaded_color = imageLoad (u_source_image, ivec2(gl_FragCoord));\n" " imageStore(u_destination_image, ivec2(gl_FragCoord), loaded_color);\n" "\n" " discard;\n" "}\n"; /* Vertex postions for "full screen" quad, defined as a triangle strip */ static const GLfloat m_vertex_buffer_data[] = { -1.0f, -1.0f, 0.0f, 1.0f, /* left bottom */ -1.0f, 1.0f, 0.0f, 1.0f, /* left top */ 1.0f, -1.0f, 0.0f, 1.0f, /* right bottom */ 1.0f, 1.0f, 0.0f, 1.0f, /* right top */ }; /* Result of BuildProgram operation */ bool is_program_correct = true; /* BuildProgram set false when it fails, but it does not set true on success */ /* Add all tested texture shapes */ int texture_edge = de::min(64, de::min(getWindowHeight(), getWindowWidth())); m_texture_shape_definitions.push_back( TextureShapeDefinition(texture_edge /* edge */, 6 /* n_elements */, GL_TEXTURE_2D_ARRAY)); m_texture_shape_definitions.push_back( TextureShapeDefinition(texture_edge /* edge */, 6 /* n_elements */, GL_TEXTURE_3D)); m_texture_shape_definitions.push_back( TextureShapeDefinition(texture_edge /* edge */, 1 /* n_elements */, GL_TEXTURE_CUBE_MAP)); m_texture_shape_definitions.push_back( TextureShapeDefinition(texture_edge /* edge */, 3 /* n_elements */, GL_TEXTURE_CUBE_MAP_ARRAY)); /* Prepare buffer with vertex positions of "full screen" quad" */ glGenBuffers(1, &m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenBuffers"); glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertex_buffer_data), m_vertex_buffer_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(glGetError(), "BufferData"); /* Generate vertex array object */ glGenVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenVertexArrays"); /* Prepare program object */ m_program_id = BuildProgram(vertex_shader_code, 0 /* src_tcs */, 0 /* src_tes */, 0 /* src_gs */, fragment_shader_code, &is_program_correct); if (false == is_program_correct) { return ERROR; } /* Done */ return NO_ERROR; } virtual long Cleanup() { /* Reset OpenGL state */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindTexture(GL_TEXTURE_3D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); glBindVertexArray(0); glUseProgram(0); /* Delete program */ if (0 != m_program_id) { glDeleteProgram(m_program_id); m_program_id = 0; } /* Delete textures */ if (0 != m_destination_texture_id) { glDeleteTextures(1, &m_destination_texture_id); m_destination_texture_id = 0; } if (0 != m_source_texture_id) { glDeleteTextures(1, &m_source_texture_id); m_source_texture_id = 0; } /* Delete vertex array object */ if (0 != m_vertex_array_object_id) { glDeleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } /* Delete buffer */ if (0 != m_vertex_buffer_id) { glDeleteBuffers(1, &m_vertex_buffer_id); m_vertex_buffer_id = 0; } /* Done */ return NO_ERROR; } virtual long Run() { bool result = true; /* For each shape */ for (TextureShapeDefinitionList::const_iterator it = m_texture_shape_definitions.begin(); m_texture_shape_definitions.end() != it; ++it) { const GLuint n_layers = GetTotalNumberOfLayers(it->m_n_elements, it->m_type); /* Prepare "source" and "destination" textures */ GLU_EXPECT_NO_ERROR( CreateRGBA8DestinationTexture(it->m_edge, it->m_n_elements, it->m_type, m_destination_texture_id), "Create2DR8UIDestinationTexture"); GLU_EXPECT_NO_ERROR(CreateRGBA8SourceTexture(it->m_edge, it->m_n_elements, it->m_type, m_source_texture_id), "Create2DR8UISourceTexture"); /* Copy texture data with imageLoad() and imageStore() operations */ CopyRGBA8Texture(m_destination_texture_id, m_source_texture_id, n_layers); /* Compare "source" and "destination" textures */ if (false == CompareRGBA8Textures(m_destination_texture_id, m_source_texture_id, it->m_edge, n_layers, it->m_type)) { const char* texture_type = ""; switch (it->m_type) { case GL_TEXTURE_2D_ARRAY: texture_type = "2d array"; break; case GL_TEXTURE_3D: texture_type = "3d"; break; case GL_TEXTURE_CUBE_MAP: texture_type = "Cube map"; break; case GL_TEXTURE_CUBE_MAP_ARRAY: texture_type = "Cube map array"; break; } m_context.getTestContext().getLog() << tcu::TestLog::Message << "Copy with imageLoad and imageStore failed for texture type: " << texture_type << ". Source and destination textures are different" << tcu::TestLog::EndMessage; result = false; } /* Destroy "source" and "destination" textures */ glDeleteTextures(1, &m_destination_texture_id); glDeleteTextures(1, &m_source_texture_id); m_destination_texture_id = 0; m_source_texture_id = 0; } if (false == result) { return ERROR; } /* Done */ return NO_ERROR; } private: /* Private methods */ /** Binds a texture to user-specified image unit and update relevant sampler uniform * * @param program_id Program object id * @param texture_id Texture id * @param image_unit Index of image unit * @param layer Index of layer bound to unit * @param uniform_name Name of image uniform **/ void BindTextureToImage(GLuint program_id, GLuint texture_id, GLuint image_unit, GLuint layer, const char* uniform_name) { static const GLint invalid_uniform_location = -1; GLint image_uniform_location = 0; /* Get uniform location */ image_uniform_location = glGetUniformLocation(program_id, uniform_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (invalid_uniform_location == image_uniform_location) { throw tcu::InternalError("Uniform location is not available", uniform_name, __FILE__, __LINE__); } /* Bind texture to image unit */ glBindImageTexture(image_unit, texture_id, 0 /* level */, GL_FALSE /* layered */, layer, GL_READ_WRITE, GL_RGBA8); GLU_EXPECT_NO_ERROR(glGetError(), "BindImageTexture"); /* Set uniform to image unit */ glUniform1i(image_uniform_location, image_unit); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); } /** Compare two 2D R8UI textures * * @param left_texture_id Id of "left" texture object * @param right_texture_id Id of "right" texture object * @param edge Length of texture edge * @param n_layers Number of layers to compare * @param type Type of texture * * @return true when texture data is found identical, false otherwise **/ bool CompareRGBA8Textures(GLuint left_texture_id, GLuint right_texture_id, GLuint edge, GLuint n_layers, GLenum type) { static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = edge * edge * n_layers * n_components; /* Storage for texture data */ std::vector left_texture_data; std::vector right_texture_data; ExtractTextureData(left_texture_id, edge, n_layers, type, left_texture_data); ExtractTextureData(right_texture_id, edge, n_layers, type, right_texture_data); /* Compare texels */ return (0 == memcmp(&left_texture_data[0], &right_texture_data[0], texture_data_size)); } /** Copy contents of "source" texture to "destination" texture with imageLoad() and imageStore() operations * * @param destination_texture_id Id of "destination" texture object * @param source_texture_id Id of "source" texture object * @param n_layers Number of layers **/ void CopyRGBA8Texture(GLuint destination_texture_id, GLuint source_texture_id, GLuint n_layers) { for (GLuint layer = 0; layer < n_layers; ++layer) { CopyRGBA8TextureLayer(destination_texture_id, source_texture_id, layer); } } /** Copy contents of layer from "source" texture to "destination" texture with imageLoad() and imageStore() operations * * @param destination_texture_id Id of "destination" texture object * @param source_texture_id Id of "source" texture object * @param layer Index of layer **/ void CopyRGBA8TextureLayer(GLuint destination_texture_id, GLuint source_texture_id, GLuint layer) { /* Uniform names */ static const char* const destination_image_uniform_name = "u_destination_image"; static const char* const source_image_uniform_name = "u_source_image"; /* Attribute name */ static const char* const position_attribute_name = "vs_in_position"; /* Attribute location and invalid value */ static const GLint invalid_attribute_location = -1; GLint position_attribute_location = 0; /* Set current program */ glUseProgram(m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "UseProgram"); /* Bind vertex array object */ glBindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindVertexArray"); /* Bind buffer with quad vertex positions */ glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); /* Set up vertex attribute array for position attribute */ position_attribute_location = glGetAttribLocation(m_program_id, position_attribute_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetAttribLocation"); if (invalid_attribute_location == position_attribute_location) { throw tcu::InternalError("Attribute location is not available", position_attribute_name, __FILE__, __LINE__); } glVertexAttribPointer(position_attribute_location, 4 /*size */, GL_FLOAT, GL_FALSE /* normalized */, 0 /* stride */, 0 /* pointer */); GLU_EXPECT_NO_ERROR(glGetError(), "VertexAttribPointer"); glEnableVertexAttribArray(position_attribute_location); GLU_EXPECT_NO_ERROR(glGetError(), "EnableVertexAttribArray"); /* Set up textures as source and destination image samplers */ BindTextureToImage(m_program_id, destination_texture_id, 0 /* image_unit */, layer, destination_image_uniform_name); BindTextureToImage(m_program_id, source_texture_id, 1 /* image_unit */, layer, source_image_uniform_name); /* Execute draw */ glDrawArrays(GL_TRIANGLE_STRIP, 0 /* first vertex */, 4 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); } /** Creates RGBA8 texture of given type and fills it with zeros * * @param edge Edge of created texture * @param n_elements Number of elements in texture array * @param target Target of created texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum CreateRGBA8DestinationTexture(GLuint edge, GLuint n_elements, GLenum target, GLuint& out_texture_id) { /* Constasts to calculate texture size */ static const GLuint n_components = 4; /* RGBA */ const GLuint layer_size = edge * edge * n_components; const GLuint n_layers = GetTotalNumberOfLayers(n_elements, target); const GLuint texture_size = layer_size * n_layers; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_size); /* Set all texels */ for (GLuint i = 0; i < texture_size; ++i) { texture_data[i] = 0; } /* Create texture */ return CreateRGBA8Texture(edge, target, n_layers, texture_data, out_texture_id); } /** Creates RGBA8 texture and fills it with [x, y, layer, 0xaa] * * @param edge Edge of created texture * @param n_elements Number of elements in texture array * @param target Target of created texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum CreateRGBA8SourceTexture(GLuint edge, GLuint n_elements, GLenum target, GLuint& out_texture_id) { /* Constants to calculate texture size */ static const GLuint n_components = 4; /* RGBA */ const GLuint layer_size = edge * edge * n_components; const GLuint n_layers = GetTotalNumberOfLayers(n_elements, target); const GLuint texture_size = layer_size * n_layers; /* Value of texel */ GLubyte texel[4] = { 0, 0, 0, 0xaa }; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_size); /* Set all texels */ for (GLuint layer = 0; layer < n_layers; ++layer) { const GLuint layer_offset = layer_size * layer; texel[2] = static_cast(layer); for (GLuint y = 0; y < edge; ++y) { const GLuint line_offset = y * edge * n_components + layer_offset; texel[1] = static_cast(y); for (GLuint x = 0; x < edge; ++x) { const GLuint texel_offset = x * n_components + line_offset; texel[0] = static_cast(x); for (GLuint component = 0; component < n_components; ++component) { texture_data[texel_offset + component] = texel[component]; } } } } /* Create texture */ return CreateRGBA8Texture(edge, target, n_layers, texture_data, out_texture_id); } /** Creates RGBA8 texture of given type and fills it provided data * * @param edge Edge of created texture * @param n_elements Number of elements in texture array * @param target Target of created texture * @param texture_data Texture data * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum CreateRGBA8Texture(GLuint edge, GLenum target, GLuint n_layers, const std::vector& texture_data, GLuint& out_texture_id) { GLenum err = 0; GLuint texture_id = 0; /* Generate texture */ glGenTextures(1, &texture_id); err = glGetError(); if (GL_NO_ERROR != err) { return err; } /* Bind texture */ glBindTexture(target, texture_id); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Allocate storage and fill texture */ if (GL_TEXTURE_CUBE_MAP != target) { glTexImage3D(target, 0 /* level */, GL_RGBA8, edge, edge, n_layers, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); } else { const GLuint n_components = 4; const GLuint layer_size = edge * edge * n_components; glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0 * layer_size]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[1 * layer_size]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[2 * layer_size]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[3 * layer_size]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[4 * layer_size]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0 /* level */, GL_RGBA8, edge, edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[5 * layer_size]); } err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Make texture complete */ glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Set out_texture_id */ out_texture_id = texture_id; /* Done */ return GL_NO_ERROR; } /** Extracts texture data * * @param texture_id Id of texture object * @param edge Length of texture edge * @param n_layers Number of layers * @param target Target of texture * @param texture_data Extracted texture data **/ void ExtractTextureData(GLuint texture_id, GLuint edge, GLuint n_layers, GLenum target, std::vector& texture_data) { static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = edge * edge * n_layers * n_components; /* Alocate memory for texture data */ texture_data.resize(texture_data_size); /* Bind texture */ glBindTexture(target, texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); /* Get data */ if (GL_TEXTURE_CUBE_MAP != target) { glGetTexImage(target, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); } else { const GLuint layer_size = edge * edge * n_components; glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0 * layer_size]); glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[1 * layer_size]); glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[2 * layer_size]); glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[3 * layer_size]); glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[4 * layer_size]); glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[5 * layer_size]); } GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); } /** Get number of layers per single element for given type of texture * * @param target Target of texture * * @return Number of layers **/ GLuint GetLayersPerElement(GLenum target) { switch (target) { case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_3D: return 1; case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_ARRAY: return 6; default: throw tcu::InternalError("Not supported texture type", "", __FILE__, __LINE__); } } /** Get total number of layers in texture of given type and number of array elements * * @param n_elements Number of elements in texture array * @param target Target of texture * * @return Number of layers **/ GLuint GetTotalNumberOfLayers(GLuint n_elements, GLenum target) { return GetLayersPerElement(target) * n_elements; } }; /** Test "imageLoad() and imageStore() for incomplete textures" description follows. * * Load from incomplete textures should return 0. * Store to incomplete textures should be ignored. * * Steps: * - create two incomplete textures: "incomplete_source" and * "incomplete_destination", * - create two complete textures: "complete_source" and * "complete_destination", * - fill all textures with unique values, * - prepare program that will: * * load texel from "incomplete_source" and store its value to * "complete_destination", * * load texel from "complete_source" and store its value to * "incomplete_destination". * - bind textures to corresponding image uniforms * - execute program for all texels, * - verify that "incomplete_destination" was not modified and * "complete_destination" is filled with zeros. * * Texture is considered incomplete when it has enabled mipmaping (see below) * and does not have all mipmap levels defined. But for the case of Image * accessing, it is considered invalid if it is mipmap-incomplete and the * level is different to the base level (base-incomplete). * * Creation of incomplete texture: * - generate and bind texture object id, * - call TexImage2D with : 0, * - set GL_TEXTURE_MIN_FILTER? parameter to GL_NEAREST_MIPMAP_LINEAR, (to make * sure, it should be initial value), * - set GL_TEXTURE_BASE_LEVEL parameter to 0. * - set GL_TEXTURE_MAX_LEVEL parameter, for 64x64 set 7 (log2(min(width, * height)). * * Creation of complete texture: * - generate and bind texture object id, * - call TexImage2D with : 0, * - set GL_TEXTURE_BASE_LEVEL parameter to 0. * - set GL_TEXTURE_MAX_LEVEL parameter to 0. * * Binding: * - Set level == base_level for complete destinations. * - Set level != base_level for incomplete destinations that are using * mipmap-incomplete textures. * * Test with 2D 64x64 RGBA8 textures. * * Program should consist of vertex and fragment shader. Vertex shader should * pass vertex position through. Fragment shader should do imageLoad() and * imageStore() operations at coordinates gl_FragCoord. **/ class ImageLoadStoreIncompleteTexturesTest : public ShaderImageLoadStoreBase { private: /* Constants */ /* Magic numbers that will identify textures, which will be used as their * texel value. */ static const GLubyte m_complete_destination_magic_number = 0x11; static const GLubyte m_complete_source_magic_number = 0x22; static const GLubyte m_incomplete_destination_magic_number = 0x33; static const GLubyte m_incomplete_source_magic_number = 0x44; /* Texture edge */ GLuint m_texture_edge; /* Fields */ GLuint m_complete_destination_texture_id; GLuint m_complete_source_texture_id; GLuint m_incomplete_destination_texture_id; GLuint m_incomplete_source_texture_id; GLuint m_program_id; GLuint m_vertex_array_object_id; GLuint m_vertex_buffer_id; public: /* Constructor */ ImageLoadStoreIncompleteTexturesTest() : m_texture_edge(0) , m_complete_destination_texture_id(0) , m_complete_source_texture_id(0) , m_incomplete_destination_texture_id(0) , m_incomplete_source_texture_id(0) , m_program_id(0) , m_vertex_array_object_id(0) , m_vertex_buffer_id(0) { /* Nothing to be done here */ } /* Methods inherited from SubcaseBase */ virtual long Setup() { /* Shaders code */ const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in vec4 vs_in_position;\n" "\n" "void main()\n" "{\n" " gl_Position = vs_in_position;\n" "}\n"; const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(rgba8) writeonly uniform image2D u_complete_destination_image;\n" "layout(rgba8) readonly uniform image2D u_complete_source_image;\n" "layout(rgba8) writeonly uniform image2D u_incomplete_destination_image;\n" "layout(rgba8) readonly uniform image2D u_incomplete_source_image;\n" "\n" "void main()\n" "{\n" " vec4 complete_loaded_color = imageLoad (u_complete_source_image, ivec2(gl_FragCoord));\n" " vec4 incomplete_loaded_color = imageLoad (u_incomplete_source_image, ivec2(gl_FragCoord));\n" " imageStore(u_complete_destination_image,\n" " ivec2(gl_FragCoord),\n" " incomplete_loaded_color);\n" " imageStore(u_incomplete_destination_image,\n" " ivec2(gl_FragCoord),\n" " complete_loaded_color);\n" "\n" " discard;\n" "}\n"; /* Vertex postions for "full screen" quad, made with triangle strip */ static const GLfloat m_vertex_buffer_data[] = { -1.0f, -1.0f, 0.0f, 1.0f, /* left bottom */ -1.0f, 1.0f, 0.0f, 1.0f, /* left top */ 1.0f, -1.0f, 0.0f, 1.0f, /* right bottom */ 1.0f, 1.0f, 0.0f, 1.0f, /* right top */ }; /* Result of BuildProgram operation */ bool is_program_correct = true; /* BuildProgram set false when it fails, but it does not set true on success */ /* Clean previous error */ glGetError(); m_texture_edge = de::min(64, de::min(getWindowHeight(), getWindowWidth())); /* Prepare textures */ GLU_EXPECT_NO_ERROR( Create2DRGBA8CompleteTexture(m_complete_destination_magic_number, m_complete_destination_texture_id), "Create2DRGBA8CompleteTexture"); GLU_EXPECT_NO_ERROR(Create2DRGBA8CompleteTexture(m_complete_source_magic_number, m_complete_source_texture_id), "Create2DRGBA8CompleteTexture"); GLU_EXPECT_NO_ERROR( Create2DRGBA8IncompleteTexture(m_incomplete_destination_magic_number, m_incomplete_destination_texture_id), "Create2DRGBA8IncompleteTexture"); GLU_EXPECT_NO_ERROR( Create2DRGBA8IncompleteTexture(m_incomplete_source_magic_number, m_incomplete_source_texture_id), "Create2DRGBA8IncompleteTexture"); /* Prepare buffer with vertex positions of "full screen" quad" */ glGenBuffers(1, &m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenBuffers"); glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertex_buffer_data), m_vertex_buffer_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(glGetError(), "BufferData"); /* Generate vertex array object */ glGenVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenVertexArrays"); /* Prepare program object */ m_program_id = BuildProgram(vertex_shader_code, 0 /* src_tcs */, 0 /* src_tes */, 0 /*src_gs */, fragment_shader_code, &is_program_correct); if (false == is_program_correct) { return ERROR; } /* Done */ return NO_ERROR; } virtual long Cleanup() { /* Reset OpenGL state */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindVertexArray(0); glUseProgram(0); /* Delete program */ if (0 != m_program_id) { glDeleteProgram(m_program_id); m_program_id = 0; } /* Delete textures */ if (0 != m_complete_destination_texture_id) { glDeleteTextures(1, &m_complete_destination_texture_id); m_complete_destination_texture_id = 0; } if (0 != m_complete_source_texture_id) { glDeleteTextures(1, &m_complete_source_texture_id); m_complete_source_texture_id = 0; } if (0 != m_incomplete_destination_texture_id) { glDeleteTextures(1, &m_incomplete_destination_texture_id); m_incomplete_destination_texture_id = 0; } if (0 != m_incomplete_source_texture_id) { glDeleteTextures(1, &m_incomplete_source_texture_id); m_incomplete_source_texture_id = 0; } /* Delete vertex array object */ if (0 != m_vertex_array_object_id) { glDeleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } /* Delete buffer */ if (0 != m_vertex_buffer_id) { glDeleteBuffers(1, &m_vertex_buffer_id); m_vertex_buffer_id = 0; } /* Done */ return NO_ERROR; } virtual long Run() { bool result = true; /* Copy textures data with imageLoad() and imageStore() operations */ Copy2DRGBA8Textures(m_complete_destination_texture_id, m_incomplete_destination_texture_id, m_complete_source_texture_id, m_incomplete_source_texture_id); glMemoryBarrier(GL_ALL_BARRIER_BITS); /* Verify that store to "incomplete destination" was ignored */ if (true == CheckIfTextureWasModified(m_incomplete_destination_texture_id, m_incomplete_destination_magic_number)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with imageStore, operation modified contents of incomplete texture" << tcu::TestLog::EndMessage; result = false; } /* Verify that load from "incomplete source" returned 0 */ if (false == CheckIfTextureIsBlack(m_complete_destination_texture_id)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with imageLoad, operation returned non 0 result for incomplete texture" << tcu::TestLog::EndMessage; result = false; } if (false == result) { return ERROR; } /* Done */ return NO_ERROR; } private: /* Private methods */ /** Bind texture to image unit and sets image uniform to that unit * * @param program_id Program object id * @param texture_id Texture id * @param level Texture level * @param image_unit Index of image unit * @param uniform_name Name of image uniform **/ void BindTextureToImage(GLuint program_id, GLuint texture_id, GLint level, GLuint image_unit, const char* uniform_name) { /* Uniform location and invalid value */ static const GLint invalid_uniform_location = -1; GLint image_uniform_location = 0; /* Get uniform location */ image_uniform_location = glGetUniformLocation(program_id, uniform_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (invalid_uniform_location == image_uniform_location) { throw tcu::InternalError("Uniform location is not available", uniform_name, __FILE__, __LINE__); } /* Bind texture to image unit */ glBindImageTexture(image_unit, texture_id, level, GL_FALSE, 0 /* layer */, GL_READ_WRITE, GL_RGBA8); GLU_EXPECT_NO_ERROR(glGetError(), "BindImageTexture"); /* Set uniform to image unit */ glUniform1i(image_uniform_location, image_unit); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); } /** Check if texture is filled with black color, zeros * * @param texture_id Id of texture object * * @return true when texture is fully black, false otherwise **/ bool CheckIfTextureIsBlack(GLuint texture_id) { /* Constants to calculate size of texture */ static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = m_texture_edge * m_texture_edge * n_components; /* Storage for texture data */ std::vector black_texture_data; std::vector texture_data; /* Allocate memory */ black_texture_data.resize(texture_data_size); texture_data.resize(texture_data_size); /* Set all texels to black */ for (GLuint i = 0; i < texture_data_size; ++i) { black_texture_data[i] = 0; } /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); /* Compare texels */ return (0 == memcmp(&texture_data[0], &black_texture_data[0], texture_data_size)); } /** Check if texture was modified * * @param texture_id Id of texture object * @param nagic_number Magic number that was to create texture * * @return true if texture contents match expected values, false otherwise **/ bool CheckIfTextureWasModified(GLuint texture_id, GLubyte magic_number) { /* Constants to calculate size of texture */ static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = m_texture_edge * m_texture_edge * n_components; /* Storage for texture data */ std::vector expected_texture_data; std::vector texture_data; /* Allocate memory */ expected_texture_data.resize(texture_data_size); texture_data.resize(texture_data_size); /* Prepare expected texels */ for (GLuint y = 0; y < m_texture_edge; ++y) { const GLuint line_offset = y * m_texture_edge * n_components; for (GLuint x = 0; x < m_texture_edge; ++x) { const GLuint texel_offset = x * n_components + line_offset; SetTexel(&expected_texture_data[texel_offset], static_cast(x), static_cast(y), magic_number); } } /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); /* Compare texels, true when textures are different */ return (0 != memcmp(&texture_data[0], &expected_texture_data[0], texture_data_size)); } /** Copy contents of "source" textures to "destination" textures with imageLoad() and imageStore() operations * * @param complete_destination_texture_id Id of "complete destination" texture object * @param incomplete_destination_texture_id Id of "incomplete destination" texture object * @param complete_source_texture_id Id of "complete source" texture object * @param incomplete_source_texture_id Id of "incomplete source" texture object **/ void Copy2DRGBA8Textures(GLuint complete_destination_texture_id, GLuint incomplete_destination_texture_id, GLuint complete_source_texture_id, GLuint incomplete_source_texture_id) { /* Uniform names */ static const char* const complete_destination_image_uniform_name = "u_complete_destination_image"; static const char* const complete_source_image_uniform_name = "u_complete_source_image"; static const char* const incomplete_destination_image_uniform_name = "u_incomplete_destination_image"; static const char* const incomplete_source_image_uniform_name = "u_incomplete_source_image"; /* Attribute name */ static const char* const position_attribute_name = "vs_in_position"; /* Attribute location and invalid value */ static const GLint invalid_attribute_location = -1; GLint position_attribute_location = 0; /* Set current program */ glUseProgram(m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "UseProgram"); /* Bind vertex array object */ glBindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindVertexArray"); /* Bind buffer with quad vertex positions */ glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindBuffer"); /* Setup position attribute */ position_attribute_location = glGetAttribLocation(m_program_id, position_attribute_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetAttribLocation"); if (invalid_attribute_location == position_attribute_location) { throw tcu::InternalError("Attribute location is not available", position_attribute_name, __FILE__, __LINE__); } glVertexAttribPointer(position_attribute_location, 4 /*size */, GL_FLOAT, GL_FALSE, 0 /* stride */, 0); GLU_EXPECT_NO_ERROR(glGetError(), "VertexAttribPointer"); glEnableVertexAttribArray(position_attribute_location); GLU_EXPECT_NO_ERROR(glGetError(), "EnableVertexAttribArray"); /* Setup textures as source and destination images */ BindTextureToImage(m_program_id, complete_destination_texture_id, 0 /* texture level */, 0 /* image_unit */, complete_destination_image_uniform_name); BindTextureToImage(m_program_id, complete_source_texture_id, 0 /* texture level */, 1 /* image_unit */, complete_source_image_uniform_name); BindTextureToImage(m_program_id, incomplete_destination_texture_id, 2 /* texture level */, 2 /* image_unit */, incomplete_destination_image_uniform_name); BindTextureToImage(m_program_id, incomplete_source_texture_id, 2 /* texture level */, 3 /* image_unit */, incomplete_source_image_uniform_name); /* Execute draw */ glDrawArrays(GL_TRIANGLE_STRIP, 0 /* first vertex */, 4 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); } /** Create complete 2D RGBA8 texture. * * @param magic_number Magic number of texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum Create2DRGBA8CompleteTexture(GLubyte magic_number, GLuint& out_texture_id) { /* Constants to calculate size of texture */ static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = m_texture_edge * m_texture_edge * n_components; /* Error code */ GLenum err = 0; /* Texture id */ GLuint texture_id = 0; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_data_size); /* Prepare texture data */ for (GLuint y = 0; y < m_texture_edge; ++y) { const GLuint line_offset = y * m_texture_edge * n_components; for (GLuint x = 0; x < m_texture_edge; ++x) { const GLuint texel_offset = x * n_components + line_offset; SetTexel(&texture_data[texel_offset], static_cast(x), static_cast(y), magic_number); } } /* Generate texture */ glGenTextures(1, &texture_id); err = glGetError(); if (GL_NO_ERROR != err) { return err; } /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Allocate storage and fill texture */ glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA8, m_texture_edge, m_texture_edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Make texture complete */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Set out_texture_id */ out_texture_id = texture_id; /* Done */ return GL_NO_ERROR; } /** Create incomplete 2D RGBA8 texture * * @param magic_number Magic number of texture * @param out_texture_id Id of created texture, not modified if operation fails * * @return GL_NO_ERROR if operation was successful, GL error code otherwise **/ GLenum Create2DRGBA8IncompleteTexture(GLubyte magic_number, GLuint& out_texture_id) { /* Constants to calculate size of texture */ static const GLuint n_components = 4; /* RGBA */ const GLuint texture_data_size = m_texture_edge * m_texture_edge * n_components; /* Error code */ GLenum err = 0; /* Texture id */ GLuint texture_id = 0; /* Prepare storage for texture data */ std::vector texture_data; texture_data.resize(texture_data_size); /* Prepare texture data */ for (GLuint y = 0; y < m_texture_edge; ++y) { const GLuint line_offset = y * m_texture_edge * n_components; for (GLuint x = 0; x < m_texture_edge; ++x) { const GLuint texel_offset = x * n_components + line_offset; SetTexel(&texture_data[texel_offset], static_cast(x), static_cast(y), magic_number); } } /* Generate texture */ glGenTextures(1, &texture_id); err = glGetError(); if (GL_NO_ERROR != err) { return err; } /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Allocate storage and fill texture */ glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA8, m_texture_edge, m_texture_edge, 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, &texture_data[0]); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Make texture incomplete */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); err = glGetError(); if (GL_NO_ERROR != err) { glDeleteTextures(1, &texture_id); return err; } /* Set out_texture_id */ out_texture_id = texture_id; /* Done */ return GL_NO_ERROR; } /** Prepare "unique" texels. * Texel is assigned with such values: [x_coordinate, y_coordinate, magic_number, 0xcc]. * * @param texel Storage of texel * @param x_coordinate X coordiante of texel * @param y_coordinate Y coordinate of texel * @param magic_number Magic number of texture **/ void SetTexel(GLubyte texel[4], GLubyte x_coordinate, GLubyte y_coordinate, GLubyte magic_number) { texel[0] = x_coordinate; texel[1] = y_coordinate; texel[2] = magic_number; texel[3] = 0xcc; } }; /** Test "Refer to the same image unit using multiple uniforms", description follows. * * Steps: * - prepare program object, see details below, * - prepare 2D R32I texture, width should be equal to the number of image * uniforms used by program object, height should be 2, fill first row with * unique values, fill second row with zeros, * - bind texture to first image unit, * - set all image uniforms to first image unit, * - execute program for a single vertex, * - verify that: * - values in first row were negated, * - values from first row were copied to second row, * * Repeat steps to test all shader stages that support at least 2 image * uniforms. * * Program has to contain all necessary shader stages. Use boilerplate shaders * for shader stages that are not important for the test. * * Tested shader stage should: * - Use as many different image formats as possible, image formats compatible * with R32I: * * rg16f * * r11f_g11f_b10f * * r32f * * rgb10_a2ui * * rgba8ui * * rg16ui * * r32ui * * rgba8i * * rg16i * * r32i * * rgb10_a2 * * rgba8 * * rg16 * * rgba8_snorm * * rg16_snorm. * - Declare maximum allowed number of image uniforms, * * layout(format) uniform gimage2D u_image; * * where is selected image format, is type of 2D image * compatible with and is unique name of uniform. * Note that image uniforms cannot be declared as array, due to different image * formats. Therefore separate uniforms have to be used. * - Include following code snippet: * for (int i = 0; i < gl_Max*ImageUniforms; ++i) * { * vec row_1_coord(i,0); * vec row_2_coord(i,1); * * row_1_value = imageLoad(u_image[i], row_1_coord); * imageStore(u_image[i], row_1_coord, -row_1_value); * imageStore(u_image[i], row_2_coord, row_1_value); * } * where gl_Max*ImageUniforms is the constant corresponding to tested shader * stage. **/ class ImageLoadStoreMultipleUniformsTest : public ShaderImageLoadStoreBase { private: /* Types */ /** Details of image format * **/ struct imageFormatDetails { typedef bool (*verificationRoutine)(GLint, GLint, GLint); const char* m_image_format; const char* m_image_type; const char* m_color_type; GLenum m_image_unit_format; verificationRoutine m_verification_routine; }; template struct Masks { /** Get mask of bits used to store in bit-field * * @return Mask **/ static inline T RawMask() { static const T mask = ValueMask() << OFFSET; return mask; } /** Get mask of bits used to store value. * * @return Mask **/ static inline T ValueMask() { static const T mask = (1 << SIZE) - 1; return mask; } /** Get offset. * * @return offset **/ static inline T Offset() { return OFFSET; } }; template struct Masks { /** Get mask of bits used to store in bit-field * * @return Mask **/ static inline T RawMask() { DE_ASSERT(DE_FALSE && "Shouldn't be called"); return 0; } /** Get mask of bits used to store value. * * @return Mask **/ static inline T ValueMask() { DE_ASSERT(DE_FALSE && "Shouldn't be called"); return 0; } /** Get offset. * * @return offset **/ static inline T Offset() { DE_ASSERT(DE_FALSE && "Shouldn't be called"); return 0; } }; /** Template class for accessing integer values stored in bit-fields * **/ template class Integer { public: /** Constructor * **/ Integer(T raw) : m_raw(raw) { } /** Extract value from bit-field * * @return Value **/ T Get() const { const T mask = Masks::RawMask(); const T bits = m_raw & mask; const T result = (unsigned)bits >> Masks::Offset(); return result; } /** Extract value from bit-field and negate it * * @return Negated value **/ T GetNegated() const { const T mask = Masks::ValueMask(); const T value = Get(); return Clamp((~value) + 1) & mask; } T Clamp(T n) const { const bool isUnsigned = (T(0) < T(-1)); const T min = T(isUnsigned ? 0.L : -pow(2.L, int(SIZE - 1))); const T max = T(isUnsigned ? pow(2.L, int(SIZE)) - 1.L : pow(2.L, int(SIZE - 1)) - 1.L); const T x = n > max ? max : n; return x < min ? min : x; } private: T m_raw; }; /* Enums */ /** Shader stage identification * **/ enum shaderStage { fragmentShaderStage = 2, geometryShaderStage = 4, tesselationControlShaderStage = 8, tesselationEvalutaionShaderStage = 16, vertexShaderStage = 32, }; /** Test result * **/ enum testResult { testFailed = -1, testNotSupported = 1, testPassed = 0 }; /* Constants */ static const GLint m_min_required_image_uniforms = 2; /* Fields */ GLuint m_program_to_test_fs_stage_id; GLuint m_program_to_test_gs_stage_id; GLuint m_program_to_test_tcs_stage_id; GLuint m_program_to_test_tes_stage_id; GLuint m_program_to_test_vs_stage_id; GLuint m_texture_to_test_fs_stage_id; GLuint m_texture_to_test_gs_stage_id; GLuint m_texture_to_test_tcs_stage_id; GLuint m_texture_to_test_tes_stage_id; GLuint m_texture_to_test_vs_stage_id; GLuint m_vertex_array_object_id; public: /* Constructor */ ImageLoadStoreMultipleUniformsTest() : m_program_to_test_fs_stage_id(0) , m_program_to_test_gs_stage_id(0) , m_program_to_test_tcs_stage_id(0) , m_program_to_test_tes_stage_id(0) , m_program_to_test_vs_stage_id(0) , m_texture_to_test_fs_stage_id(0) , m_texture_to_test_gs_stage_id(0) , m_texture_to_test_tcs_stage_id(0) , m_texture_to_test_tes_stage_id(0) , m_texture_to_test_vs_stage_id(0) , m_vertex_array_object_id(0) { /* Nothing to be done here */ } /* Methods inherited from SubcaseBase */ virtual long Setup() { /* Prepare programs */ m_program_to_test_fs_stage_id = buildProgramToTestShaderStage(fragmentShaderStage); m_program_to_test_gs_stage_id = buildProgramToTestShaderStage(geometryShaderStage); m_program_to_test_tcs_stage_id = buildProgramToTestShaderStage(tesselationControlShaderStage); m_program_to_test_tes_stage_id = buildProgramToTestShaderStage(tesselationEvalutaionShaderStage); m_program_to_test_vs_stage_id = buildProgramToTestShaderStage(vertexShaderStage); /* Prepare textures */ m_texture_to_test_fs_stage_id = createTextureToTestShaderStage(fragmentShaderStage); m_texture_to_test_gs_stage_id = createTextureToTestShaderStage(geometryShaderStage); m_texture_to_test_tcs_stage_id = createTextureToTestShaderStage(tesselationControlShaderStage); m_texture_to_test_tes_stage_id = createTextureToTestShaderStage(tesselationEvalutaionShaderStage); m_texture_to_test_vs_stage_id = createTextureToTestShaderStage(vertexShaderStage); /* Generate vertex array object */ glGenVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenVertexArrays"); /* Bind vertex array object */ glBindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindVertexArray"); /* Set vertices number for patches */ glPatchParameteri(GL_PATCH_VERTICES, 1); /* Done */ return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); /* Delete programs */ if (0 != m_program_to_test_fs_stage_id) { glDeleteProgram(m_program_to_test_fs_stage_id); m_program_to_test_fs_stage_id = 0; } if (0 != m_program_to_test_gs_stage_id) { glDeleteProgram(m_program_to_test_gs_stage_id); m_program_to_test_gs_stage_id = 0; } if (0 != m_program_to_test_tcs_stage_id) { glDeleteProgram(m_program_to_test_tcs_stage_id); m_program_to_test_tcs_stage_id = 0; } if (0 != m_program_to_test_tes_stage_id) { glDeleteProgram(m_program_to_test_tes_stage_id); m_program_to_test_tes_stage_id = 0; } if (0 != m_program_to_test_vs_stage_id) { glDeleteProgram(m_program_to_test_vs_stage_id); m_program_to_test_vs_stage_id = 0; } /* Delete textures */ if (0 != m_texture_to_test_fs_stage_id) { glDeleteTextures(1, &m_texture_to_test_fs_stage_id); m_texture_to_test_fs_stage_id = 0; } if (0 != m_texture_to_test_gs_stage_id) { glDeleteTextures(1, &m_texture_to_test_gs_stage_id); m_texture_to_test_gs_stage_id = 0; } if (0 != m_texture_to_test_tcs_stage_id) { glDeleteTextures(1, &m_texture_to_test_tcs_stage_id); m_texture_to_test_tcs_stage_id = 0; } if (0 != m_texture_to_test_tes_stage_id) { glDeleteTextures(1, &m_texture_to_test_tes_stage_id); m_texture_to_test_tes_stage_id = 0; } if (0 != m_texture_to_test_vs_stage_id) { glDeleteTextures(1, &m_texture_to_test_vs_stage_id); m_texture_to_test_vs_stage_id = 0; } /* Delete vertex array object id */ if (0 != m_vertex_array_object_id) { glDeleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } /* Done */ return NO_ERROR; } virtual long Run() { bool result = true; if (testFailed == testShaderStage(fragmentShaderStage)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problems with fragment shader stage!" << tcu::TestLog::EndMessage; result = false; } if (testFailed == testShaderStage(geometryShaderStage)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problems with geometry shader stage!" << tcu::TestLog::EndMessage; result = false; } if (testFailed == testShaderStage(tesselationControlShaderStage)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problems with tesselation control shader stage!" << tcu::TestLog::EndMessage; result = false; } if (testFailed == testShaderStage(tesselationEvalutaionShaderStage)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problems with tesselation evaluation shader stage!" << tcu::TestLog::EndMessage; result = false; } if (testFailed == testShaderStage(vertexShaderStage)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problems with vertex shader stage!" << tcu::TestLog::EndMessage; result = false; } if (false == result) { return ERROR; } /* Done */ return NO_ERROR; } private: /* Static routines */ /** Provide image format details for given index * * @param index Index * @param out_details Image format detail instance **/ static void getImageUniformDeclarationDetails(GLuint index, imageFormatDetails& out_details) { static const ImageLoadStoreMultipleUniformsTest::imageFormatDetails default_format_details = { "r32i", "iimage2D", "ivec4", GL_R32I, ImageLoadStoreMultipleUniformsTest::verifyInteger }; static const ImageLoadStoreMultipleUniformsTest::imageFormatDetails format_details[] = { { "rg16f", "image2D", "vec4", GL_RG16F, ImageLoadStoreMultipleUniformsTest::verifyFloat }, { "r11f_g11f_b10f", "image2D", "vec4", GL_R11F_G11F_B10F, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned }, { "r32f", "image2D", "vec4", GL_R32F, ImageLoadStoreMultipleUniformsTest::verifyFloat }, { "rgb10_a2", "image2D", "vec4", GL_RGB10_A2, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned }, { "rgba8", "image2D", "vec4", GL_RGBA8, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned }, { "rg16", "image2D", "vec4", GL_RG16, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned }, { "rgba8_snorm", "image2D", "vec4", GL_RGBA8_SNORM, ImageLoadStoreMultipleUniformsTest::verifyFloatSignedNorm }, { "rg16_snorm", "image2D", "vec4", GL_RG16_SNORM, ImageLoadStoreMultipleUniformsTest::verifyFloatSignedNorm }, { "rgb10_a2ui", "uimage2D", "uvec4", GL_RGB10_A2UI, ImageLoadStoreMultipleUniformsTest::verifyInteger<10, 10, 10, 2, GLuint> }, { "rgba8ui", "uimage2D", "uvec4", GL_RGBA8UI, ImageLoadStoreMultipleUniformsTest::verifyInteger<8, 8, 8, 8, GLuint> }, { "rg16ui", "uimage2D", "uvec4", GL_RG16UI, ImageLoadStoreMultipleUniformsTest::verifyInteger<16, 16, 0, 0, GLuint> }, { "r32ui", "uimage2D", "uvec4", GL_R32UI, ImageLoadStoreMultipleUniformsTest::verifyInteger }, { "rgba8i", "iimage2D", "ivec4", GL_RGBA8I, ImageLoadStoreMultipleUniformsTest::verifyInteger<8, 8, 8, 8, GLint> }, { "rg16i", "iimage2D", "ivec4", GL_RG16I, ImageLoadStoreMultipleUniformsTest::verifyInteger<16, 16, 0, 0, GLint> } }; static const GLuint n_imageUniformFormatDetails = sizeof(format_details) / sizeof(ImageLoadStoreMultipleUniformsTest::imageFormatDetails); if (n_imageUniformFormatDetails <= index) { out_details = default_format_details; } else { out_details = format_details[index]; } } /** Write name of image uniform at given index to output stream * * @param stream Output stream * @param index Index **/ static void writeImageUniformNameToStream(std::ostream& stream, GLuint index) { /* u_image_0 */ stream << "u_image_" << index; } /** Write name of variable used to store value loaded from image at given index to output stream * * @param stream Output stream * @param index Index **/ static void writeLoadedValueVariableNameToStream(std::ostream& stream, GLuint index) { /* loaded_value_0 */ stream << "loaded_value_" << index; } /** Write name of variable used to store coordinate of texel at given row to output stream * * @param stream Output stream * @param index Index of image uniform * @param row Row of image **/ static void writeCoordinatesVariableNameToStream(std::ostream& stream, GLuint index, GLuint row) { /* row_0_coordinates_0 */ stream << "row_" << row << "_coordinates_" << index; } struct imageUniformDeclaration { imageUniformDeclaration(GLuint index) : m_index(index) { } GLuint m_index; }; /** Write declaration of image uniform at given index to output stream * * @param stream Output stream * @param imageUniformDeclaration Declaration details * * @return stream **/ friend std::ostream& operator<<(std::ostream& stream, const imageUniformDeclaration& declaration) { ImageLoadStoreMultipleUniformsTest::imageFormatDetails format_details; getImageUniformDeclarationDetails(declaration.m_index, format_details); /* layout(r32f) uniform image2D u_image_0; */ stream << "layout(" << format_details.m_image_format << ") uniform " << format_details.m_image_type << " "; ImageLoadStoreMultipleUniformsTest::writeImageUniformNameToStream(stream, declaration.m_index); stream << ";"; return stream; } struct imageLoadCall { imageLoadCall(GLuint index) : m_index(index) { } GLuint m_index; }; /* Stream operators */ /** Write code that execute imageLoad routine for image at given index to output stream * * @param stream Output stream * @param load imageLoad call details * * @return stream **/ friend std::ostream& operator<<(std::ostream& stream, const imageLoadCall& load) { ImageLoadStoreMultipleUniformsTest::imageFormatDetails format_details; getImageUniformDeclarationDetails(load.m_index, format_details); /* vec4 loaded_value_0 = imageLoad(u_image_0, row_0_coordinates_0); */ stream << format_details.m_color_type << " "; writeLoadedValueVariableNameToStream(stream, load.m_index); stream << " = imageLoad("; writeImageUniformNameToStream(stream, load.m_index); stream << ", "; writeCoordinatesVariableNameToStream(stream, load.m_index, 0 /* row */); stream << ");"; return stream; } struct imageStoreCall { imageStoreCall(GLuint index, GLuint row) : m_index(index), m_row(row) { } GLuint m_index; GLuint m_row; }; /** Write code that execute imageStore to image at given index to output stream * * @param stream Output stream * @param store imageStore call details * * @return stream **/ friend std::ostream& operator<<(std::ostream& stream, const imageStoreCall& store) { /* imageStore(u_image_0, row_0_coordinates_0, -loaded_value_0); */ stream << "imageStore("; writeImageUniformNameToStream(stream, store.m_index); stream << ", "; writeCoordinatesVariableNameToStream(stream, store.m_index, store.m_row); if (0 == store.m_row) { stream << ", -"; } else { stream << ", "; } writeLoadedValueVariableNameToStream(stream, store.m_index); stream << ");"; return stream; } struct coordinatesVariableDeclaration { coordinatesVariableDeclaration(GLuint index, GLuint row) : m_index(index), m_row(row) { } GLuint m_index; GLuint m_row; }; /** Write declaration of variable for coordinate at given row to output stream * * @param stream Output stream * @param declaration Declaration details * * @return stream **/ friend std::ostream& operator<<(std::ostream& stream, const coordinatesVariableDeclaration& declaration) { stream << "const ivec2 "; writeCoordinatesVariableNameToStream(stream, declaration.m_index, declaration.m_row); stream << " = ivec2(" << declaration.m_index << ", " << declaration.m_row << ");"; return stream; } /* Methods */ /** Build program to test specified shader stage * * Throws exception in case of any failure * * @param stage Stage id * * @return Program id **/ GLuint buildProgramToTestShaderStage(shaderStage stage) { static const char* const boilerplate_fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n"; static const char* const boilerplate_tesselation_evaluation_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(quads, equal_spacing, ccw) in;\n" "\n" "void main()\n" "{\n" "\n" "}\n"; static const char* const boilerplate_vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(location = 0) in vec4 i_position;\n" "\n" "void main()\n" "{\n" " gl_Position = i_position;\n" "}\n"; const char* fragment_shader_code = boilerplate_fragment_shader_code; const char* geometry_shader_code = 0; bool is_program_built = true; GLuint program_object_id = 0; const char* tesselation_control_shader_code = 0; const char* tesselation_evaluation_shader_code = 0; std::string tested_shader_stage_code; const char* vertex_shader_code = boilerplate_vertex_shader_code; /* Get source code for tested shader stage */ prepareShaderForTestedShaderStage(stage, tested_shader_stage_code); if (true == tested_shader_stage_code.empty()) { return 0; } /* Set up source code for all required stages */ switch (stage) { case fragmentShaderStage: fragment_shader_code = tested_shader_stage_code.c_str(); break; case geometryShaderStage: geometry_shader_code = tested_shader_stage_code.c_str(); break; case tesselationControlShaderStage: tesselation_control_shader_code = tested_shader_stage_code.c_str(); tesselation_evaluation_shader_code = boilerplate_tesselation_evaluation_shader_code; break; case tesselationEvalutaionShaderStage: tesselation_evaluation_shader_code = tested_shader_stage_code.c_str(); break; case vertexShaderStage: vertex_shader_code = tested_shader_stage_code.c_str(); break; default: TCU_FAIL("Invalid shader stage"); } /* Build program */ program_object_id = BuildProgram(vertex_shader_code, tesselation_control_shader_code, tesselation_evaluation_shader_code, geometry_shader_code, fragment_shader_code, &is_program_built); /* Check if program was built */ if (false == is_program_built) { throw tcu::InternalError("Failed to build program", "", __FILE__, __LINE__); } /* Done */ return program_object_id; } /** Create texture to test given shader stage * * Throws exception in case of any failure * * @param stage Stage id * * @return Texture id **/ GLuint createTextureToTestShaderStage(shaderStage stage) { GLenum error = glGetError(); const GLint max_image_uniforms = getMaximumImageUniformsForStage(stage); GLuint texture_id = 0; std::vector texture_data; const GLsizei height = 2; const GLsizei width = max_image_uniforms; if (m_min_required_image_uniforms > max_image_uniforms) { return 0; } /* Generate texture id */ glGenTextures(1, &texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenTextures"); /* Bind texture */ glBindTexture(GL_TEXTURE_2D, texture_id); error = glGetError(); if (GL_NO_ERROR != error) { glDeleteTextures(1, &texture_id); GLU_EXPECT_NO_ERROR(error, "BindTexture"); } /* Prepare storage for texture data */ texture_data.resize(width * height); for (GLint i = 0; i < max_image_uniforms; ++i) { texture_data[i] = getExpectedValue(i); texture_data[i + width] = 0; } /* Create first level of texture */ glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_R32I, width, height, 0 /*border */, GL_RED_INTEGER, GL_INT, &texture_data[0]); error = glGetError(); if (GL_NO_ERROR != error) { glDeleteTextures(1, &texture_id); GLU_EXPECT_NO_ERROR(error, "TexImage2D"); } /* Make texture complete */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); error = glGetError(); if (GL_NO_ERROR != error) { glDeleteTextures(1, &texture_id); GLU_EXPECT_NO_ERROR(error, "TexParameteri"); } /* Done */ return texture_id; } /** Get value of texel for image at given index * * @param index Index of image uniform * * @return Value of texel **/ GLint getExpectedValue(GLint index) { // To fix denorm issues with r32f, rg16f and r11f_g11f_b10f // we set one bit in the exponent of each component of those pixel format return 0x40104200 + index; } /** Get name of uniform at given index * * @param index Index of uniform * @param out_name Name of uniform **/ void getImageUniformName(GLuint index, std::string& out_name) { std::stringstream stream; writeImageUniformNameToStream(stream, index); out_name = stream.str(); } /** Get maximum number of image uniforms allowed for given shader stage * * @param stage Stage id * * @return Maximum allowed image uniforms **/ GLint getMaximumImageUniformsForStage(shaderStage stage) { GLint max_image_uniforms = 0; GLenum pname = 0; switch (stage) { case fragmentShaderStage: pname = GL_MAX_FRAGMENT_IMAGE_UNIFORMS; break; case geometryShaderStage: pname = GL_MAX_GEOMETRY_IMAGE_UNIFORMS; break; case tesselationControlShaderStage: pname = GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS; break; case tesselationEvalutaionShaderStage: pname = GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS; break; case vertexShaderStage: pname = GL_MAX_VERTEX_IMAGE_UNIFORMS; break; default: TCU_FAIL("Invalid shader stage"); } glGetIntegerv(pname, &max_image_uniforms); GLU_EXPECT_NO_ERROR(glGetError(), "GetIntegerv"); return max_image_uniforms; } /** Prepare source for tested shader stage * * @param stage Stage id * @param out_code Source code **/ void prepareShaderForTestedShaderStage(shaderStage stage, std::string& out_code) { GLint max_image_uniforms = getMaximumImageUniformsForStage(stage); const char* stage_specific_layout = ""; const char* stage_specific_predicate = "true"; std::stringstream stream; if (m_min_required_image_uniforms > max_image_uniforms) { return; } /* Expected result follows * * #version 400 core * #extension GL_ARB_shader_image_load_store : require * * precision highp float; * * stage_specific_layout goes here * * Uniform declarations go here * * void main() * { * const ivec2 row_0_coordinates(0, 0); * const ivec2 row_1_coordinates(0, 1); * * For each index <0, GL_MAX_*_IMAGE_UNIFORMS> * * vec4 loaded_value_0 = imageLoad(u_image_0, row_0_coordinates); * * imageStore(u_image_0, row_0_coordinates, -loaded_value_0); * imageStore(u_image_0, row_1_coordinates, loaded_value_0); * } */ /* Get piece of code specific for stage */ switch (stage) { case fragmentShaderStage: break; case geometryShaderStage: stage_specific_layout = "layout(points) in;\n" "layout(points, max_vertices = 1) out;\n" "\n"; break; case tesselationControlShaderStage: stage_specific_layout = "layout(vertices = 4) out;\n" "\n"; stage_specific_predicate = "gl_InvocationID == 0"; break; case tesselationEvalutaionShaderStage: stage_specific_layout = "layout(quads, equal_spacing, ccw) in;\n" "\n"; break; case vertexShaderStage: break; default: TCU_FAIL("Invalid shader stage"); } /* Preamble */ stream << "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" << stage_specific_layout; /* Image uniforms declarations */ for (GLint i = 0; i < max_image_uniforms; ++i) { stream << imageUniformDeclaration(i) << "\n"; } /* Main opening */ stream << "\n" "void main()\n" "{\n"; stream << " if (" << stage_specific_predicate << ")\n"; stream << " {\n"; /* imageLoad and imageStores for each uniform */ for (GLint i = 0; i < max_image_uniforms; ++i) { stream << " " << coordinatesVariableDeclaration(i, 0) << "\n" << " " << coordinatesVariableDeclaration(i, 1) << "\n" << "\n" << " " << imageLoadCall(i) << "\n" << "\n" << " " << imageStoreCall(i, 0) << "\n" << " " << imageStoreCall(i, 1) << "\n"; if (max_image_uniforms > i + 1) { stream << "\n"; } } stream << " }\n"; /* Main closing */ stream << "}\n\n"; /* Done */ out_code = stream.str(); } /** Test given shader stage * * @param stage Stage id * * @return m_test_not_supported if shader stage does not support at least m_min_required_image_uniforms image uniforms; * testFailed when test result is negative; * m_test_passed when test result is positive; **/ testResult testShaderStage(shaderStage stage) { std::string image_uniform_name; static const GLint invalid_uniform_location = -1; const GLint max_image_uniforms = getMaximumImageUniformsForStage(stage); GLenum primitive_mode = GL_POINTS; GLuint program_id = 0; testResult result = testPassed; std::vector texture_data; GLuint texture_id = 0; static const GLuint height = 2; const GLuint width = max_image_uniforms; const GLuint positive_value_index = width; static const GLuint negated_value_index = 0; if (m_min_required_image_uniforms > max_image_uniforms) { return testNotSupported; } /* Select program and texture ids for given stage */ switch (stage) { case fragmentShaderStage: program_id = m_program_to_test_fs_stage_id; texture_id = m_texture_to_test_fs_stage_id; break; case geometryShaderStage: program_id = m_program_to_test_gs_stage_id; texture_id = m_texture_to_test_gs_stage_id; break; case tesselationControlShaderStage: primitive_mode = GL_PATCHES; program_id = m_program_to_test_tcs_stage_id; texture_id = m_texture_to_test_tcs_stage_id; break; case tesselationEvalutaionShaderStage: primitive_mode = GL_PATCHES; program_id = m_program_to_test_tes_stage_id; texture_id = m_texture_to_test_tes_stage_id; break; case vertexShaderStage: program_id = m_program_to_test_vs_stage_id; texture_id = m_texture_to_test_vs_stage_id; break; default: TCU_FAIL("Invalid shader stage"); } /* Set program */ glUseProgram(program_id); GLU_EXPECT_NO_ERROR(glGetError(), "UseProgram"); /* Bind texture to image units */ for (GLint i = 0; i < max_image_uniforms; ++i) { imageFormatDetails format_details; getImageUniformDeclarationDetails(i, format_details); glBindImageTexture(i /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, GL_READ_WRITE, format_details.m_image_unit_format); GLU_EXPECT_NO_ERROR(glGetError(), "BindImageTexture"); } /* Set all image uniforms to corresponding image units */ for (GLint i = 0; i < max_image_uniforms; ++i) { /* Get name */ getImageUniformName(i, image_uniform_name); /* Get location */ GLint image_uniform_location = glGetUniformLocation(program_id, image_uniform_name.c_str()); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (invalid_uniform_location == image_uniform_location) { throw tcu::InternalError("Uniform location is not available", image_uniform_name.c_str(), __FILE__, __LINE__); } /* Set uniform value */ glUniform1i(image_uniform_location, i /* image_unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); } /* Execute draw */ glDrawArrays(primitive_mode, 0 /* first vertex */, 1 /* one vertex */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); texture_data.resize(width * height); /* Get texture data */ glBindTexture(GL_TEXTURE_2D, texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, &texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); /* Verify each image uniform */ for (GLint i = 0; i < max_image_uniforms; ++i) { imageFormatDetails format_details; getImageUniformDeclarationDetails(i, format_details); if (false == format_details.m_verification_routine(getExpectedValue(i), texture_data[positive_value_index + i], texture_data[negated_value_index + i])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid result!" << " Image format: " << format_details.m_image_format << " Original value: " << "0x" << std::setw(8) << std::setfill('0') << std::setbase(16) << getExpectedValue(i) << " Copied value: " << "0x" << std::setw(8) << std::setfill('0') << std::setbase(16) << texture_data[positive_value_index + i] << " Negated value: " << "0x" << std::setw(8) << std::setfill('0') << std::setbase(16) << texture_data[negated_value_index + i] << tcu::TestLog::EndMessage; result = testFailed; } } /* Done */ return result; } /** Verifies if original_value, positive_value and negated_value match * * @tparam T Type used during verification process, it should match float values by size * * @param original_value Original value of texel, used when creating a texture * @param positive_value Value stored by shader as read * @param negated_value Value stored by shader after negation * * @return true if values match, false otherwise **/ template static bool verifyFloat(GLint original_value, GLint positive_value, GLint negated_value) { if (original_value != positive_value) { return false; } static const GLuint n_elements = sizeof(GLint) / sizeof(T); /* 1, 2, 4 */ static const GLuint sign_bit_index = sizeof(T) * 8 - 1; /* 7, 15, 31 */ static const T sign_bit_mask = 1 << sign_bit_index; /* 0x80.. */ static const T sign_bit_inv_mask = (T)~sign_bit_mask; /* 0x7f.. */ const T* positive_elements = (T*)&positive_value; const T* negated_elements = (T*)&negated_value; for (GLuint i = 0; i < n_elements; ++i) { const T positive_element = positive_elements[i]; const T negated_element = negated_elements[i]; const T positive_sign_bit = positive_element & sign_bit_mask; const T negated_sign_bit = negated_element & sign_bit_mask; const T positive_data = positive_element & sign_bit_inv_mask; const T negated_data = negated_element & sign_bit_inv_mask; /* Compare data bits */ if (positive_data != negated_data) { return false; } /* Verify that sign bit is inverted */ if (positive_sign_bit == negated_sign_bit) { return false; } } return true; } /** Verifies if original_value, positive_value and negated_value match * * @tparam T Type used during verification process, it should match float values by size * * @param original_value Original value of texel, used when creating a texture * @param positive_value Value stored by shader as read * @param negated_value Value stored by shader after negation * * @return true if values match, false otherwise **/ template static bool verifyFloatSignedNorm(GLint original_value, GLint positive_value, GLint negated_value) { if (original_value != positive_value) { return false; } static const GLuint n_elements = sizeof(GLint) / sizeof(T); /* 1, 2, 4 */ const T* positive_elements = (T*)&positive_value; const T* negated_elements = (T*)&negated_value; for (GLuint i = 0; i < n_elements; ++i) { const T positive_element = positive_elements[i]; const T negated_element = negated_elements[i]; /* Compare data bits */ if (positive_element != -negated_element) { return false; } } return true; } /** Verifies if original_value, positive_value and negated_value match * * @tparam R Number of bits for red channel * @tparam G Number of bits for green channel * @tparam B Number of bits for blue channel * @tparam A Number of bits for alpha channel * * @param original_value Original value of texel, used when creating a texture * @param positive_value Value stored by shader as read * @param negated_value Value stored by shader after negation * * @return true if values match, false otherwise **/ template static bool verifyInteger(GLint original_value, GLint positive_value, GLint negated_value) { if (original_value != positive_value) { return false; } Integer positive_red(positive_value); Integer negated_red(negated_value); Integer positive_green(positive_value); Integer negated_green(negated_value); Integer positive_blue(positive_value); Integer negated_blue(negated_value); Integer positive_alpha(positive_value); Integer negated_alpha(negated_value); if (((0 != R) && (positive_red.GetNegated() != negated_red.Get())) || ((0 != B) && (positive_blue.GetNegated() != negated_blue.Get())) || ((0 != G) && (positive_green.GetNegated() != negated_green.Get())) || ((0 != A) && (positive_alpha.GetNegated() != negated_alpha.Get()))) { return false; } return true; } /** Verifies if original_value, positive_value and negated_value match * * @param original_value Original value of texel, used when creating a texture * @param positive_value Value stored by shader as read * @param negated_value Value stored by shader after negation * * @return true if values match, false otherwise **/ static bool verifyInteger(GLint original_value, GLint positive_value, GLint negated_value) { if (original_value != positive_value) { return false; } if (positive_value != -negated_value) { return false; } return true; } /** Verifies if original_value, positive_value and negated_value match * * @param original_value Original value of texel, used when creating a texture * @param positive_value Value stored by shader as read * @param negated_value Value stored by shader after negation * * @return true if values match, false otherwise **/ template static bool verifyFloatUnsigned(GLint original_value, GLint positive_value, GLint negated_value) { if (original_value != positive_value) { return false; } if (0 != negated_value) { return false; } return true; } }; /** Test "Early fragment tests" description follows. * * BasicGLSLEarlyFragTests verifies that: * - early z test is applied when enabled, * - early z test is not applied when disabled. * * Proposed modifications: * - verify that early z test does not discard all fragments when enabled, * - verify that early stencil test is applied when enabled, * - verify that early stencil test does not discard all fragments when * enabled, * - verify that early stencil test is not applied when disabled. * * Steps: * - prepare 2 programs that store 1.0 at red channel to image in fragment * shader stage: * a) one program should enable early fragment tests * ("layout(early_fragment_tests) in;"), * b) second program should disable early fragment tests, * - prepare frame buffer with 64x64 R32F color and GL_DEPTH_STENCIL * depth-stencil attachments, * - prepare 2D texture 64x64 R32F, * - enable depth test, * - verify that early z test is applied when enabled: * - use program enabling early fragment tests, * - clean frame buffer with color: 0, stencil: 0 and depth 0.5, * - fill texture with zeros, * - bind texture to image uniform, * - draw "full screen" quad (left bottom corner at -1,-1 and right top * corner at 1,1) at z: 0.75 * - verify that texture is still filled with zeros, * - verify that early z test does not discard all fragments: * - use program enabling early fragment tests, * - clean frame buffer with color: 0, stencil: 0 and depth 0.5, * - fill texture with zeros, * - bind texture to image uniform, * - draw "full screen" quad at z: 0.25 * - verify that texture is now filled with 1.0, * -verify that early z test is not applied when disabled: * - use program disabling early fragment tests, * - clean frame buffer with color: 0, stencil: 0 and depth 0.5, * - fill texture with zeros, * - bind texture to image uniform, * - draw "full screen" quad at z: 0.75 * - verify that texture is now filled with 1.0. * - disable depth test * - enable stencil test * - verify that early stencil test is applied when enabled: * - use program enabling early fragment tests, * - clean frame buffer with color: 0, stencil: 0 and depth 1, * - fill texture with zeros, * - set stencil test to: * - to GL_LESS, * - to 128, * - 0xffffffff, * - bind texture to image uniform, * - draw "full screen" quad at z: 0, * - verify that texture is still filled with zeros, * - verify that early stencil test does not discard all fragments: * - use program enabling early fragment tests, * - clean frame buffer with color: 0, stencil: 128 and depth 1, * - fill texture with zeros, * - set stencil test to: * - to GL_LESS, * - to 0, * - 0xffffffff, * - bind texture to image uniform, * - draw "full screen" quad at z: 0, * - verify that texture is now filled with 1.0, * - verify that early stencil test is not applied when disabled: * - use program disabling early fragment tests, * - clean frame buffer with color: 0, stencil: 0 and depth 1, * - fill texture with zeros, * - set stencil test to: * - to GL_LESS, * - to 128, * - 0xffffffff, * - bind texture to image uniform, * - draw "full screen" quad at z: 0, * - verify that texture is now filled with 1.0 **/ class ImageLoadStoreEarlyFragmentTestsTest : public ShaderImageLoadStoreBase { private: /* Constants */ GLuint m_image_edge; static const GLint m_invalid_uniform_location = -1; /* Types */ /** Store id and uniform locations for a single program object * **/ struct programDetails { GLint m_depth_uniform_location; GLint m_image_uniform_location; GLuint m_program_id; programDetails() : m_depth_uniform_location(ImageLoadStoreEarlyFragmentTestsTest::m_invalid_uniform_location) , m_image_uniform_location(ImageLoadStoreEarlyFragmentTestsTest::m_invalid_uniform_location) , m_program_id(0) { /* Nothing to be done here */ } }; /* Fileds */ /* Storage for texture data */ std::vector m_clean_texture_data; std::vector m_extracted_texture_data; /* Program details */ programDetails m_disabled_early_tests; programDetails m_enabled_early_tests; /* Ids of GL objects */ GLuint m_color_renderbuffer_id; GLuint m_depth_stencil_renderbuffer_id; GLuint m_framebuffer_id; GLuint m_texture_id; GLuint m_vertex_array_object_id; public: /* Constructor */ ImageLoadStoreEarlyFragmentTestsTest() : m_image_edge(0) , m_color_renderbuffer_id(0) , m_depth_stencil_renderbuffer_id(0) , m_framebuffer_id(0) , m_texture_id(0) , m_vertex_array_object_id(0) { /* Nothing to be done here */ } /* Methods inherited from SubcaseBase */ virtual long Cleanup() { /* Restore defaults */ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); glBindVertexArray(0); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glUseProgram(0); /* Delete objects */ if (0 != m_disabled_early_tests.m_program_id) { glDeleteProgram(m_disabled_early_tests.m_program_id); m_disabled_early_tests.m_program_id = 0; } if (0 != m_enabled_early_tests.m_program_id) { glDeleteProgram(m_enabled_early_tests.m_program_id); m_enabled_early_tests.m_program_id = 0; } if (0 != m_color_renderbuffer_id) { glDeleteRenderbuffers(1, &m_color_renderbuffer_id); m_color_renderbuffer_id = 0; } if (0 != m_depth_stencil_renderbuffer_id) { glDeleteRenderbuffers(1, &m_depth_stencil_renderbuffer_id); m_depth_stencil_renderbuffer_id = 0; } if (0 != m_framebuffer_id) { glDeleteFramebuffers(1, &m_framebuffer_id); m_framebuffer_id = 0; } if (0 != m_texture_id) { glDeleteTextures(1, &m_texture_id); m_texture_id = 0; } if (0 != m_vertex_array_object_id) { glDeleteVertexArrays(1, &m_vertex_array_object_id); m_vertex_array_object_id = 0; } /* Done */ return NO_ERROR; } virtual long Run() { bool result = true; /* Bind texture to first image unit */ glBindImageTexture(0 /* first image unit */, m_texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, GL_READ_WRITE, GL_R32F); GLU_EXPECT_NO_ERROR(glGetError(), "BindImageTexture"); /* Run tests for depth test */ if (false == testEarlyZ()) { result = false; } /* Run tests for stencil test */ if (false == testEarlyStencil()) { result = false; } /* Return ERROR if any problem was found */ if (false == result) { return ERROR; } /* Done */ return NO_ERROR; } virtual long Setup() { m_image_edge = de::min(64, de::min(getWindowHeight(), getWindowWidth())); /* Prepare storage for texture data */ m_clean_texture_data.resize(m_image_edge * m_image_edge); m_extracted_texture_data.resize(m_image_edge * m_image_edge); /* Prepare programs, framebuffer and texture */ buildPrograms(); createFramebuffer(); createTexture(); /* Generate vertex array object */ glGenVertexArrays(1, &m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenVertexArrays"); /* Bind vertex array object */ glBindVertexArray(m_vertex_array_object_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindVertexArray"); /* Set clear color */ glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GLU_EXPECT_NO_ERROR(glGetError(), "ClearColor"); /* Done */ return NO_ERROR; } private: /** Build two programs: with enabled and disabled early fragment tests * **/ void buildPrograms() { static const char* const fragment_shader_with_disabled_early_tests = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(r32f) uniform image2D u_image;\n" "\n" "void main()\n" "{\n" " vec4 color = vec4(1.0, 0, 0, 0);\n" "\n" " imageStore(u_image, ivec2(gl_FragCoord.xy), color);\n" "\n" " discard;\n" "}\n\n"; static const char* const fragment_shader_with_enabled_early_tests = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(early_fragment_tests) in;\n" "\n" "layout(r32f) uniform image2D u_image;\n" "\n" "void main()\n" "{\n" " vec4 color = vec4(1.0, 0, 0, 0);\n" "\n" " imageStore(u_image, ivec2(gl_FragCoord.xy), color);\n" "\n" " discard;\n" "}\n\n"; static const char* const geometry_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(points) in;\n" "layout(triangle_strip, max_vertices = 4) out;\n" "\n" "uniform float u_depth;\n" "\n" "void main()\n" "{\n" " // Left-bottom\n" " gl_Position = vec4(-1, -1, u_depth, 1);\n" " EmitVertex();\n" "\n" " // Left-top\n" " gl_Position = vec4(-1, 1, u_depth, 1);\n" " EmitVertex();\n" "\n" " // Right-bottom\n" " gl_Position = vec4( 1, -1, u_depth, 1);\n" " EmitVertex();\n" "\n" " // Right-top\n" " gl_Position = vec4( 1, 1, u_depth, 1);\n" " EmitVertex();\n" "}\n\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" "}\n\n"; prepareProgramDetails(fragment_shader_with_disabled_early_tests, geometry_shader_code, vertex_shader_code, m_disabled_early_tests); prepareProgramDetails(fragment_shader_with_enabled_early_tests, geometry_shader_code, vertex_shader_code, m_enabled_early_tests); } /** Fill texture with zeros * **/ void cleanTexture() { glTexSubImage2D(GL_TEXTURE_2D, 0 /* level */, 0 /* xoffset */, 0 /* yoffset */, m_image_edge, m_image_edge, GL_RED, GL_FLOAT, &m_clean_texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "TexSubImage2D"); } /** Create and bind (draw) framebuffer with color and depth-stencil attachments * **/ void createFramebuffer() { /* Generate render buffers */ glGenRenderbuffers(1, &m_color_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenRenderbuffers"); glGenRenderbuffers(1, &m_depth_stencil_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenRenderbuffers"); /* Generate and bind framebuffer object */ glGenFramebuffers(1, &m_framebuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenFramebuffers"); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindFramebuffer"); /* Prepare color render buffer */ glBindRenderbuffer(GL_RENDERBUFFER, m_color_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindRenderbuffer"); glRenderbufferStorage(GL_RENDERBUFFER, GL_R32F, m_image_edge, m_image_edge); GLU_EXPECT_NO_ERROR(glGetError(), "RenderbufferStorage"); /* Set up color attachment */ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_color_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "FramebufferRenderbuffer"); /* Prepare depth-stencil render buffer */ glBindRenderbuffer(GL_RENDERBUFFER, m_depth_stencil_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindRenderbuffer"); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_image_edge, m_image_edge); GLU_EXPECT_NO_ERROR(glGetError(), "RenderbufferStorage"); /* Set up depth-stencil attachment */ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depth_stencil_renderbuffer_id); GLU_EXPECT_NO_ERROR(glGetError(), "FramebufferRenderbuffer"); } /** Create 2D R32F texture * **/ void createTexture() { glGenTextures(1, &m_texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "GenTextures"); glBindTexture(GL_TEXTURE_2D, m_texture_id); GLU_EXPECT_NO_ERROR(glGetError(), "BindTexture"); glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, m_image_edge, m_image_edge); GLU_EXPECT_NO_ERROR(glGetError(), "TexStorage2D"); } /** Extracts red channel from texture and verify if all texels are set to specified value * * @param value Expected value * * @return true if all texel match expected value, false otherwise **/ bool isTextureFilledWithValue(GLfloat value) { glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, &m_extracted_texture_data[0]); GLU_EXPECT_NO_ERROR(glGetError(), "GetTexImage"); for (GLuint i = 0; i < m_image_edge * m_image_edge; ++i) { if (value != m_extracted_texture_data[i]) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Texel at location " << i << " has invalid value: " << m_extracted_texture_data[i] << " expected: " << value << tcu::TestLog::EndMessage; return false; } } return true; } /** Build program, extract location of uniforms and store results in programDetails instance * * Throws tcu::InternalError if uniforms are inactive * * @param fragment_shader_code Source of fragment shader * @param geometry_shader_code Source of geometry shader * @param vertex_shader_code Source of vertex shader * @param out_program_details Instance of programDetails **/ void prepareProgramDetails(const char* fragment_shader_code, const char* geometry_shader_code, const char* vertex_shader_code, programDetails& out_program_details) { static const char* const depth_uniform_name = "u_depth"; static const char* const image_uniform_name = "u_image"; bool is_program_built = true; GLuint program_id = BuildProgram(vertex_shader_code, 0 /* src_tcs */, 0 /* src_tes */, geometry_shader_code, fragment_shader_code, &is_program_built); if (false == is_program_built) { throw tcu::InternalError("Failed to build program", "", __FILE__, __LINE__); } /* Get depth uniform location */ GLint depth_uniform_location = glGetUniformLocation(program_id, depth_uniform_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (m_invalid_uniform_location == depth_uniform_location) { throw tcu::InternalError("Uniform is not active", image_uniform_name, __FILE__, __LINE__); } /* Get image uniform location */ GLint image_uniform_location = glGetUniformLocation(program_id, image_uniform_name); GLU_EXPECT_NO_ERROR(glGetError(), "GetUniformLocation"); if (m_invalid_uniform_location == image_uniform_location) { throw tcu::InternalError("Uniform is not active", image_uniform_name, __FILE__, __LINE__); } /* Store results */ out_program_details.m_depth_uniform_location = depth_uniform_location; out_program_details.m_image_uniform_location = image_uniform_location; out_program_details.m_program_id = program_id; } /** Test if early fragment stencil test works as expected. * * @return true if successful, false otherwise **/ bool testEarlyStencil() { bool result = true; glEnable(GL_STENCIL_TEST); GLU_EXPECT_NO_ERROR(glGetError(), "glEnable"); glClearDepthf(1.0f); GLU_EXPECT_NO_ERROR(glGetError(), "ClearDepthf"); /* verify that early stencil test is applied when enabled */ { glUseProgram(m_enabled_early_tests.m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "glUseProgram"); glClearStencil(0); GLU_EXPECT_NO_ERROR(glGetError(), "ClearStencil"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glStencilFunc(GL_LESS, 128, 0xffffffff); GLU_EXPECT_NO_ERROR(glGetError(), "StencilFunc"); glUniform1i(m_enabled_early_tests.m_image_uniform_location, 0 /* first unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); glUniform1f(m_enabled_early_tests.m_depth_uniform_location, 0.0f /* depth */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1f"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(0.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early stencil test. It is not applied" << tcu::TestLog::EndMessage; result = false; } } /* verify that early stencil test does not discard all fragments */ { glClearStencil(128); GLU_EXPECT_NO_ERROR(glGetError(), "ClearStencil"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glStencilFunc(GL_LESS, 0, 0xffffffff); GLU_EXPECT_NO_ERROR(glGetError(), "StencilFunc"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(1.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early stencil test. It discards fragments, that shall be drawn" << tcu::TestLog::EndMessage; result = false; } } /* verify that early stencil test is not applied when disabled */ { glUseProgram(m_disabled_early_tests.m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "glUseProgram"); glClearStencil(0); GLU_EXPECT_NO_ERROR(glGetError(), "ClearStencil"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glStencilFunc(GL_LESS, 128, 0xffffffff); GLU_EXPECT_NO_ERROR(glGetError(), "StencilFunc"); glUniform1i(m_disabled_early_tests.m_image_uniform_location, 0 /* first unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); glUniform1f(m_disabled_early_tests.m_depth_uniform_location, 0.0f /* depth */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1f"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(1.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early stencil test. It is applied when disabled" << tcu::TestLog::EndMessage; result = false; } } glDisable(GL_STENCIL_TEST); GLU_EXPECT_NO_ERROR(glGetError(), "Disable"); /* Done */ return result; } /** Test if early fragment depth test works as expected. * * @return true if successful, false otherwise **/ bool testEarlyZ() { bool result = true; glEnable(GL_DEPTH_TEST); GLU_EXPECT_NO_ERROR(glGetError(), "glEnable"); glClearDepthf(0.5f); GLU_EXPECT_NO_ERROR(glGetError(), "ClearDepthf"); glClearStencil(0); GLU_EXPECT_NO_ERROR(glGetError(), "ClearStencil"); /* verify that early z test is applied when enabled */ { glUseProgram(m_enabled_early_tests.m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "glUseProgram"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glUniform1i(m_enabled_early_tests.m_image_uniform_location, 0 /* first unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); glUniform1f(m_enabled_early_tests.m_depth_uniform_location, 0.5f /* depth */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1f"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(0.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early z test. It is not applied" << tcu::TestLog::EndMessage; result = false; } } /* verify that early z test does not discard all fragments */ { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glUniform1i(m_enabled_early_tests.m_image_uniform_location, 0 /* first unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); glUniform1f(m_enabled_early_tests.m_depth_uniform_location, -0.5f /* depth */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1f"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(1.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early z test. It discards fragments, that shall be drawn" << tcu::TestLog::EndMessage; result = false; } } /* verify that early z test is not applied when disabled */ { glUseProgram(m_disabled_early_tests.m_program_id); GLU_EXPECT_NO_ERROR(glGetError(), "glUseProgram"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLU_EXPECT_NO_ERROR(glGetError(), "Clear"); cleanTexture(); glUniform1i(m_disabled_early_tests.m_image_uniform_location, 0 /* first unit */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1i"); glUniform1f(m_disabled_early_tests.m_depth_uniform_location, 0.5f /* depth */); GLU_EXPECT_NO_ERROR(glGetError(), "Uniform1f"); glDrawArrays(GL_POINTS, 0 /* first */, 1 /* number of vertices */); GLU_EXPECT_NO_ERROR(glGetError(), "DrawArrays"); glMemoryBarrier(GL_ALL_BARRIER_BITS); GLU_EXPECT_NO_ERROR(glGetError(), "MemoryBarrier"); if (false == isTextureFilledWithValue(1.0f)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Problem with early z test. It is applied when disabled" << tcu::TestLog::EndMessage; result = false; } } glDisable(GL_DEPTH_TEST); GLU_EXPECT_NO_ERROR(glGetError(), "Disable"); /* Done */ return result; } }; //----------------------------------------------------------------------------- // 4.1 NegativeUniform //----------------------------------------------------------------------------- class NegativeUniform : public ShaderImageLoadStoreBase { GLuint m_program; virtual long Setup() { m_program = 0; return NO_ERROR; } virtual long Run() { const char* glsl_vs = "#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "void main() {" NL " gl_Position = i_position;" NL "}"; const char* glsl_fs = "#version 420 core" NL "writeonly uniform image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " imageStore(g_image, coord, vec4(0.0));" NL " discard;" NL "}"; m_program = BuildProgram(glsl_vs, NULL, NULL, NULL, glsl_fs); GLint max_image_units; glGetIntegerv(GL_MAX_IMAGE_UNITS, &max_image_units); glUseProgram(m_program); glUniform1i(glGetUniformLocation(m_program, "g_image"), -1); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform1i should generate INVALID_VALUE when is less than zero." << tcu::TestLog::EndMessage; return ERROR; } glUniform1i(glGetUniformLocation(m_program, "g_image"), max_image_units); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform1i should generate INVALID_VALUE when is greater than or equal to the value of " << "MAX_IMAGE_UNITS." << tcu::TestLog::EndMessage; return ERROR; } GLint i = -3; glUniform1iv(glGetUniformLocation(m_program, "g_image"), 1, &i); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform1iv should generate INVALID_VALUE when is less than zero." << tcu::TestLog::EndMessage; return ERROR; } i = max_image_units + 1; glUniform1iv(glGetUniformLocation(m_program, "g_image"), 1, &i); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform1iv should generate INVALID_VALUE when is greater " "than or equal to the value of MAX_IMAGE_UNITS." << tcu::TestLog::EndMessage; return ERROR; } glUniform1ui(glGetUniformLocation(m_program, "g_image"), 0); if (glGetError() != GL_INVALID_OPERATION) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform1iv should generate INVALID_OPERATION if the location refers to an image variable." << tcu::TestLog::EndMessage; return ERROR; } glUniform2i(glGetUniformLocation(m_program, "g_image"), 0, 0); if (glGetError() != GL_INVALID_OPERATION) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUniform2i should generate INVALID_OPERATION if the location refers to an image variable." << tcu::TestLog::EndMessage; return ERROR; } { glUseProgram(0); glProgramUniform1i(m_program, glGetUniformLocation(m_program, "g_image"), -1); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform1i should generate INVALID_VALUE when is less than zero." << tcu::TestLog::EndMessage; return ERROR; } glProgramUniform1i(m_program, glGetUniformLocation(m_program, "g_image"), max_image_units); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform1i should generate INVALID_VALUE when is greater than or equal to the " "value of MAX_IMAGE_UNITS." << tcu::TestLog::EndMessage; return ERROR; } GLint ii = -3; glProgramUniform1iv(m_program, glGetUniformLocation(m_program, "g_image"), 1, &ii); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform1iv should generate INVALID_VALUE when is less than zero." << tcu::TestLog::EndMessage; return ERROR; } ii = max_image_units + 1; glProgramUniform1iv(m_program, glGetUniformLocation(m_program, "g_image"), 1, &ii); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform1iv should generate INVALID_VALUE when " "is greater than or equal to the value of MAX_IMAGE_UNITS." << tcu::TestLog::EndMessage; return ERROR; } glProgramUniform1ui(m_program, glGetUniformLocation(m_program, "g_image"), 0); if (glGetError() != GL_INVALID_OPERATION) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform1ui should generate INVALID_OPERATION if the " "location refers to an image variable." << tcu::TestLog::EndMessage; return ERROR; } glProgramUniform2i(m_program, glGetUniformLocation(m_program, "g_image"), 0, 0); if (glGetError() != GL_INVALID_OPERATION) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glProgramUniform2i should generate INVALID_OPERATION if the " "location refers to an image variable." << tcu::TestLog::EndMessage; return ERROR; } } return NO_ERROR; } virtual long Cleanup() { glUseProgram(0); glDeleteProgram(m_program); return NO_ERROR; } }; //----------------------------------------------------------------------------- // 4.2 NegativeBind //----------------------------------------------------------------------------- class NegativeBind : public ShaderImageLoadStoreBase { virtual long Setup() { return NO_ERROR; } virtual long Run() { glBindImageTexture(100, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "BindImageTexture should generate INVALID_VALUE if is " "greater than or equal to the value of MAX_IMAGE_UNITS." << tcu::TestLog::EndMessage; return ERROR; } glBindImageTexture(0, 123, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "BindImageTexture should generate INVALID_VALUE if is not " "the name of an existing texture object." << tcu::TestLog::EndMessage; return ERROR; } glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA + 1234); if (glGetError() != GL_INVALID_VALUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "BindImageTexture should generate INVALID_VALUE if is not a legal format." << tcu::TestLog::EndMessage; return ERROR; } return NO_ERROR; } virtual long Cleanup() { return NO_ERROR; } }; //----------------------------------------------------------------------------- // 4.3 NegativeCompileErrors //----------------------------------------------------------------------------- class NegativeCompileErrors : public ShaderImageLoadStoreBase { virtual long Run() { if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) writeonly readonly uniform image2D g_image;" NL "void main() {" NL " o_color = imageLoad(g_image, ivec2(0));" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) writeonly uniform image2D g_image;" NL "void main() {" NL " o_color = imageLoad(g_image, ivec2(0));" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "in vec4 i_color;" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) readonly uniform image2D g_image;" NL "void main() {" NL " imageStore(g_image, ivec2(0), i_color);" NL " o_color = i_color;" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "uniform image2D g_image;" NL "void main() {" NL " o_color = imageLoad(g_image, ivec2(0));" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "readonly uniform image2D g_image;" NL "void main() {" NL " o_color = imageLoad(g_image, ivec2(0));" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rg16i) uniform image1D g_image;" NL "void main() {" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rg16) uniform iimage2D g_image;" NL "void main() {" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(r32f) coherent uniform image2D g_image;" NL "void main() {" NL " imageAtomicAdd(g_image, ivec2(1), 10);" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(r16i) coherent uniform iimage2D g_image;" NL "void main() {" NL " imageAtomicAdd(g_image, ivec2(1), 1u);" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(r32ui) uniform iimage3D g_image;" NL "void main() {" NL " imageStore(g_image, ivec3(1), ivec4(1));" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba8) uniform uimage2DArray g_image;" NL "void main() {" NL " imageStore(g_image, ivec3(0), uvec4(1));" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Compile("#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) coherent uniform image2D g_image;" NL "vec4 Load(image2D image) {" NL " return imageLoad(image, vec2(0));" NL "}" NL "void main() {" NL " o_color = Load(g_image);" NL "}")) return ERROR; return NO_ERROR; } bool Compile(const std::string& source) { const GLuint sh = glCreateShader(GL_FRAGMENT_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; } }; //----------------------------------------------------------------------------- // 4.4 NegativeLinkErrors //----------------------------------------------------------------------------- class NegativeLinkErrors : public ShaderImageLoadStoreBase { virtual long Run() { if (!SupportedInVS(1)) return NOT_SUPPORTED; if (!Link("#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(rgba32f) uniform image1D g_image;" NL "void main() {" NL " imageStore(g_image, gl_VertexID, vec4(0));" NL " gl_Position = i_position;" NL "}", "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rgba32f) uniform image2D g_image;" NL "void main() {" NL " imageStore(g_image, ivec2(gl_FragCoord), vec4(1.0));" NL " o_color = vec4(1.0);" NL "}")) return ERROR; if (!Link("#version 420 core" NL "layout(location = 0) in vec4 i_position;" NL "layout(rgba32f) uniform image1D g_image;" NL "void main() {" NL " imageStore(g_image, gl_VertexID, vec4(0));" NL " gl_Position = i_position;" NL "}", "#version 420 core" NL "layout(location = 0) out vec4 o_color;" NL "layout(rg32f) uniform image1D g_image;" NL "void main() {" NL " imageStore(g_image, int(gl_FragCoord.x), vec4(1.0));" NL " o_color = vec4(1.0);" NL "}")) return ERROR; return NO_ERROR; } bool Link(const std::string& vs, const std::string& fs) { const GLuint p = glCreateProgram(); const GLuint vsh = glCreateShader(GL_VERTEX_SHADER); glAttachShader(p, vsh); glDeleteShader(vsh); const char* const vssrc = vs.c_str(); glShaderSource(vsh, 1, &vssrc, NULL); glCompileShader(vsh); const GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER); glAttachShader(p, fsh); glDeleteShader(fsh); const char* const fssrc = fs.c_str(); glShaderSource(fsh, 1, &fssrc, NULL); glCompileShader(fsh); GLint status; glGetShaderiv(vsh, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { glDeleteProgram(p); m_context.getTestContext().getLog() << tcu::TestLog::Message << "VS compilation should be ok." << tcu::TestLog::EndMessage; return false; } glGetShaderiv(fsh, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { glDeleteProgram(p); m_context.getTestContext().getLog() << tcu::TestLog::Message << "FS compilation should be ok." << tcu::TestLog::EndMessage; return false; } glLinkProgram(p); GLchar log[1024]; glGetProgramInfoLog(p, sizeof(log), NULL, log); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program Info Log:\n" << log << tcu::TestLog::EndMessage; glGetProgramiv(p, GL_LINK_STATUS, &status); glDeleteProgram(p); if (status == GL_TRUE) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Link operation should fail." << tcu::TestLog::EndMessage; return false; } return true; } }; /** Negative Test "Active image uniform limits", description follows. * * Program that exceeds resource limits should not compile and/or link. * * Steps: * - try to compile and link a program that uses too many image uniforms in * fragment shader stage, * - try to compile and link a program that uses too many image uniforms in * vertex shader stage, * - try to compile and link a program that uses too many image uniforms in * tessellation control shader stage, * - try to compile and link a program that uses too many image uniforms in * tessellation evaluation shader stage, * - try to compile and link a program that uses too many image uniforms in * geometry shader stage, * - try to compile and link a program that uses too many image uniforms in all * shader stages combined, any single stage should not exceed its limits, this * step might be impossible to fulfill. * * Test should use the following declaration of image uniforms: * layout(r32i) uniform iimage2D u_image[N_UNIFORMS]; * * For cases where limit for single stage is tested, N_UNIFORMS should be * defined as gl_Max*ImageUniforms + 1, where gl_Max*ImageUniforms is constant * corresponding to tested shader stage. * * For case where limit for combined stages is tested: * - u_image name should be appended with the name of shader stage, like * u_image_vertex, * - N_UNIFORMS should be defined as gl_Max*ImageUniforms, where * gl_Max*ImageUniforms is constant corresponding to tested shader stage, * - compilation and linking shall succeed, when sum of all * gl_Max*ImageUniforms corresponding to shader stages is equal (or less) to * gl_MaxCombinedImageUniforms. * * All defined image uniforms have to be active. Each shader stage that declare * image uniforms should include following code snippet: * value = 1; * for (int i = 0; i < N_UNIFORMS; ++i) * { * value = imageAtomicAdd(u_image[i], coord, value); * } **/ class ImageLoadStoreUniformLimitsTest : public ShaderImageLoadStoreBase { private: /* Fields */ /* Results */ bool m_result_for_combined; bool m_result_for_fragment_shader; bool m_result_for_geometry_shader; bool m_result_for_tesselation_control_shader; bool m_result_for_tesselatioon_evaluation_shader; bool m_result_for_vertex_shader; public: /* Constructor */ ImageLoadStoreUniformLimitsTest() : m_result_for_combined(false) , m_result_for_fragment_shader(false) , m_result_for_geometry_shader(false) , m_result_for_tesselation_control_shader(false) , m_result_for_tesselatioon_evaluation_shader(false) , m_result_for_vertex_shader(false) { /* Nothing to be done */ } /* Methods inherited from SubcaseBase */ virtual long Cleanup() { /* Done */ return NO_ERROR; } virtual long Run() { m_context.getTestContext().getLog() << tcu::TestLog::Message << "This test tries to build invalid programs, expect error messages about " "exceeded number of active image uniforms" << tcu::TestLog::EndMessage; testFragmentShaderStage(); testGeometryShaderStage(); testTesselationControlShaderStage(); testTesselationEvaluationShaderStage(); testVertexShaderStage(); testCombinedShaderStages(); /* Return error if any stage failed */ if ((false == m_result_for_combined) || (false == m_result_for_fragment_shader) || (false == m_result_for_geometry_shader) || (false == m_result_for_tesselation_control_shader) || (false == m_result_for_tesselatioon_evaluation_shader) || (false == m_result_for_vertex_shader)) { return ERROR; } /* Done */ return NO_ERROR; } virtual long Setup() { /* Done */ return NO_ERROR; } private: /** Test fragment shader stage * **/ void testFragmentShaderStage() { static const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "#define N_UNIFORMS gl_MaxFragmentImageUniforms + 1\n" "\n" "flat in ivec2 vs_fs_coord;\n" "\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_fs_coord, value);\n" " }\n" "\n" " discard;\n" "}\n\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" " in ivec2 vs_in_coord;\n" "flat out ivec2 vs_fs_coord;\n" "\n" "void main()\n" "{\n" " vs_fs_coord = vs_in_coord;\n" "}\n\n"; m_result_for_fragment_shader = !doesProgramLink(fragment_shader_code, 0 /* geometry_shader_code */, 0 /* tesselation_control_shader_code */, 0 /* tesselation_evaluation_shader_code */, vertex_shader_code); if (false == m_result_for_fragment_shader) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_FRAGMENT_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Shader code:\n" << fragment_shader_code << tcu::TestLog::EndMessage; } } /** Test geometry shader stage * **/ void testGeometryShaderStage() { static const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n\n"; static const char* const geometry_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 1) out;\n" "\n" "#define N_UNIFORMS gl_MaxGeometryImageUniforms + 1\n" "\n" "in ivec2 vs_gs_coord[];\n" "\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_gs_coord[0], value);\n" " }\n" "}\n\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in ivec2 vs_in_coord;\n" "out ivec2 vs_gs_coord;\n" "\n" "void main()\n" "{\n" " vs_gs_coord = vs_in_coord;\n" "}\n\n"; m_result_for_geometry_shader = !doesProgramLink(fragment_shader_code, geometry_shader_code, 0 /* tesselation_control_shader_code */, 0 /* tesselation_evaluation_shader_code */, vertex_shader_code); if (false == m_result_for_geometry_shader) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_GEOMETRY_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Shader code:\n" << geometry_shader_code << tcu::TestLog::EndMessage; } } /** Test tesselation control shader stage * **/ void testTesselationControlShaderStage() { static const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n\n"; static const char* const tesselation_control_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(vertices = 4) out;\n" "\n" "#define N_UNIFORMS gl_MaxTessControlImageUniforms + 1\n" "\n" "in ivec2 vs_tcs_coord[];\n" "\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_tcs_coord[0], value);\n" " }\n" "}\n\n"; static const char* const tesselation_evaluation_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(quads, equal_spacing, ccw) in;\n" "\n" "void main()\n" "{\n" "}\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in ivec2 vs_in_coord;\n" "out ivec2 vs_tcs_coord;\n" "\n" "void main()\n" "{\n" " vs_tcs_coord = vs_in_coord;\n" "}\n\n"; m_result_for_tesselation_control_shader = !doesProgramLink(fragment_shader_code, 0 /* geometry_shader_code */, tesselation_control_shader_code, tesselation_evaluation_shader_code, vertex_shader_code); if (false == m_result_for_tesselation_control_shader) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Shader code:\n" << tesselation_control_shader_code << tcu::TestLog::EndMessage; } } /** Test teselation evaluation shader stage * **/ void testTesselationEvaluationShaderStage() { static const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n\n"; static const char* const tesselation_evaluation_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(quads, equal_spacing, ccw) in;\n" "\n" "#define N_UNIFORMS gl_MaxTessEvaluationImageUniforms + 1\n" "\n" "in ivec2 vs_tes_coord[];\n" "\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_tes_coord[0], value);\n" " }\n" "}\n\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in ivec2 vs_in_coord;\n" "out ivec2 vs_tes_coord;\n" "\n" "void main()\n" "{\n" " vs_tes_coord = vs_in_coord;\n" "}\n\n"; m_result_for_tesselatioon_evaluation_shader = !doesProgramLink( fragment_shader_code, 0 /* geometry_shader_code */, 0 /* tesselation_control_shader_code */, tesselation_evaluation_shader_code, vertex_shader_code); if (false == m_result_for_tesselatioon_evaluation_shader) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Shader code:\n" << tesselation_evaluation_shader_code << tcu::TestLog::EndMessage; } } /** Test vertex shader stage * **/ void testVertexShaderStage() { static const char* const fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " discard;\n" "}\n\n"; static const char* const vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "in ivec2 vs_in_coord;\n" "\n" "#define N_UNIFORMS gl_MaxVertexImageUniforms + 1\n" "\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_in_coord, value);\n" " }\n" "}\n\n"; m_result_for_vertex_shader = !doesProgramLink(fragment_shader_code, 0 /* geometry_shader_code */, 0 /* tesselation_control_shader_code */, 0 /* tesselation_evaluation_shader_code */, vertex_shader_code); if (false == m_result_for_vertex_shader) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_VERTEX_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Shader code:\n" << vertex_shader_code << tcu::TestLog::EndMessage; } } /** Test combined shader stages * **/ void testCombinedShaderStages() { std::string fragment_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "#define N_UNIFORMS gl_MaxFragmentImageUniforms\n" "\n" "flat in ivec2 gs_fs_coord;\n" "\n" "layout(r32i) uniform iimage2D u_image_fragment[N_UNIFORMS];\n" "\n" "void main()\n" "{\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image_fragment[i], gs_fs_coord, value);\n" " }\n" "\n" " discard;\n" "}\n\n"; std::string geometry_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 1) out;\n" "\n" "#define N_UNIFORMS gl_MaxGeometryImageUniforms\n" "\n" "flat in ivec2 tes_gs_coord[];\n" "flat out ivec2 gs_fs_coord;\n" "\n" "#ifdef IMAGES\n" "layout(r32i) uniform iimage2D u_image_geometry[N_UNIFORMS];\n" "#endif\n" "\n" "void main()\n" "{\n" "#ifdef IMAGES\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image_geometry[i], tes_gs_coord[0], value);\n" " }\n" "\n" "#endif\n" " gs_fs_coord = tes_gs_coord[0];\n" " EmitVertex();\n" "}\n\n"; std::string tesselation_control_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(vertices = 4) out;\n" "\n" "#define N_UNIFORMS gl_MaxTessControlImageUniforms\n" "\n" "flat in ivec2 vs_tcs_coord[];\n" "flat out ivec2 tcs_tes_coord[];\n" "\n" "#ifdef IMAGES\n" "layout(r32i) uniform iimage2D u_image_tess_control[N_UNIFORMS];\n" "#endif\n" "\n" "void main()\n" "{\n" "#ifdef IMAGES\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image_tess_control[i], vs_tcs_coord[0], value);\n" " }\n" "\n" "#endif\n" " tcs_tes_coord[gl_InvocationID] = vs_tcs_coord[0];\n" "}\n\n"; std::string tesselation_evaluation_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" "layout(quads, equal_spacing, ccw) in;\n" "\n" "#define N_UNIFORMS gl_MaxTessEvaluationImageUniforms\n" "\n" "flat in ivec2 tcs_tes_coord[];\n" "flat out ivec2 tes_gs_coord;\n" "\n" "#ifdef IMAGES\n" "layout(r32i) uniform iimage2D u_image_tess_evaluation[N_UNIFORMS];\n" "#endif\n" "\n" "void main()\n" "{\n" "#ifdef IMAGES\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image_tess_evaluation[i], tcs_tes_coord[0], value);\n" " }\n" "\n" "#endif\n" " tes_gs_coord = tcs_tes_coord[0];\n" "}\n\n"; std::string vertex_shader_code = "#version 400 core\n" "#extension GL_ARB_shader_image_load_store : require\n" "\n" "precision highp float;\n" "\n" " in ivec2 vs_in_coord;\n" "flat out ivec2 vs_tcs_coord;\n" "\n" "#define N_UNIFORMS gl_MaxVertexImageUniforms\n" "\n" "#ifdef IMAGES\n" "layout(r32i) uniform iimage2D u_image[N_UNIFORMS];\n" "#endif\n" "\n" "void main()\n" "{\n" "#ifdef IMAGES\n" " int value = 1;\n" "\n" " for (int i = 0; i < N_UNIFORMS; ++i)\n" " {\n" " value = imageAtomicAdd(u_image[i], vs_in_coord, value);\n" " }\n" "\n" "#endif\n" " vs_tcs_coord = vs_tcs_coord;\n" "}\n\n"; /* Active image uniform limits */ GLint max_combined_image_uniforms = 0; GLint max_fragment_image_uniforms = 0; GLint max_geometry_image_uniforms = 0; GLint max_tesselation_control_image_uniforms = 0; GLint max_tesselation_evaluation_image_uniforms = 0; GLint max_vertex_image_uniforms = 0; /* Get limit values */ glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &max_combined_image_uniforms); glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &max_fragment_image_uniforms); glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &max_geometry_image_uniforms); glGetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &max_tesselation_control_image_uniforms); glGetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &max_tesselation_evaluation_image_uniforms); glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &max_vertex_image_uniforms); GLU_EXPECT_NO_ERROR(glGetError(), "GetIntegerv"); if (max_vertex_image_uniforms) vertex_shader_code.insert(18, "#define IMAGES\n"); if (max_geometry_image_uniforms) geometry_shader_code.insert(18, "#define IMAGES\n"); if (max_tesselation_control_image_uniforms) tesselation_control_shader_code.insert(18, "#define IMAGES\n"); if (max_tesselation_evaluation_image_uniforms) tesselation_evaluation_shader_code.insert(18, "#define IMAGES\n"); /* Check if program builds */ m_result_for_combined = !doesProgramLink(fragment_shader_code.c_str(), geometry_shader_code.c_str(), tesselation_control_shader_code.c_str(), tesselation_evaluation_shader_code.c_str(), vertex_shader_code.c_str()); /* Result depends on the limit values */ if (max_combined_image_uniforms >= (max_fragment_image_uniforms + max_geometry_image_uniforms + max_tesselation_control_image_uniforms + max_tesselation_evaluation_image_uniforms + max_vertex_image_uniforms)) { /* In this case, combined image uniforms limit cannot be exeeded, therefore successful linking is expected */ m_result_for_combined = !m_result_for_combined; if (false == m_result_for_combined) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "There was an error while building a program." << " File: " << __FILE__ << " Line: " << __LINE__ << " Vertex shader code:\n" << vertex_shader_code << "\nTesselation control shader code:\n" << tesselation_control_shader_code << "\nTesselation evaluation shader code:\n" << tesselation_evaluation_shader_code << "\nGeometry shader code:\n" << geometry_shader_code << "\nFragment shader code:\n" << fragment_shader_code << tcu::TestLog::EndMessage; } } else { /* In this case, combined image uniforms can be exceeded, therefore failed linking is expected */ if (false == m_result_for_combined) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program which exceeds limit of GL_MAX_COMBINED_IMAGE_UNIFORMS was linked successfully." << " File: " << __FILE__ << " Line: " << __LINE__ << " Vertex shader code:\n" << vertex_shader_code << "\nTesselation control shader code:\n" << tesselation_control_shader_code << "\nTesselation evaluation shader code:\n" << tesselation_evaluation_shader_code << "\nGeometry shader code:\n" << geometry_shader_code << "\nFragment shader code:\n" << fragment_shader_code << tcu::TestLog::EndMessage; } } } /** Check if program builds successfully * * @param fragment_shader_code Source code for fragment shader stage * @param geometry_shader_code Source code for geometry shader stage * @param tesselation_control_shader_code Source code for tesselation control shader stage * @param tesselation_evaluation_shader_code Source code for tesselation evaluation shader stage * @param vertex_shader_code Source code for vertex shader stage * * @return true if program was built without errors, false otherwise **/ bool doesProgramLink(const char* fragment_shader_code, const char* geometry_shader_code, const char* tesselation_control_shader_code, const char* tesselation_evaluation_shader_code, const char* vertex_shader_code) { bool is_program_built = true; GLuint program_id = 0; program_id = BuildProgram(vertex_shader_code, tesselation_control_shader_code, tesselation_evaluation_shader_code, geometry_shader_code, fragment_shader_code, &is_program_built); if (0 != program_id) { glDeleteProgram(program_id); } return is_program_built; } }; } ShaderImageLoadStoreTests::ShaderImageLoadStoreTests(deqp::Context& context) : TestCaseGroup(context, "shader_image_load_store", "") { } ShaderImageLoadStoreTests::~ShaderImageLoadStoreTests(void) { } void ShaderImageLoadStoreTests::init() { using namespace deqp; addChild(new TestSubcase(m_context, "basic-api-get", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-api-bind", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-api-barrier", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-api-texParam", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allFormats-store", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allFormats-load", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allFormats-storeGeometryStages", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allFormats-loadGeometryStages", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allFormats-loadStoreComputeStage", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-store", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-load-nonMS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-load-ms", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-atomic", TestSubcase::Create)); addChild( new TestSubcase(m_context, "basic-allTargets-loadStoreVS", TestSubcase::Create)); addChild( new TestSubcase(m_context, "basic-allTargets-loadStoreTCS", TestSubcase::Create)); addChild( new TestSubcase(m_context, "basic-allTargets-loadStoreTES", TestSubcase::Create)); addChild( new TestSubcase(m_context, "basic-allTargets-loadStoreGS", TestSubcase::Create)); addChild( new TestSubcase(m_context, "basic-allTargets-loadStoreCS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-atomicVS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-atomicTCS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-atomicGS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-allTargets-atomicCS", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-glsl-misc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-glsl-earlyFragTests", TestSubcase::Create)); addChild(new TestSubcase(m_context, "basic-glsl-const", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-imageAccess", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-vertexArray", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-drawIndirect", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-textureUpdate", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-imageAccess2", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sync-bufferUpdate", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-allStages-oneImage", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-memory-dependentInvocation", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-memory-order", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sso-simple", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sso-atomicCounters", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sso-subroutine", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-sso-perSample", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-copyImage", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-allMips", TestSubcase::Create)); addChild(new TestSubcase(m_context, "advanced-cast", TestSubcase::Create)); addChild( new TestSubcase(m_context, "single-byte_data_alignment", TestSubcase::Create)); addChild( new TestSubcase(m_context, "non-layered_binding", TestSubcase::Create)); addChild( new TestSubcase(m_context, "incomplete_textures", TestSubcase::Create)); addChild(new TestSubcase(m_context, "multiple-uniforms", TestSubcase::Create)); addChild( new TestSubcase(m_context, "early-fragment-tests", TestSubcase::Create)); addChild(new TestSubcase(m_context, "negative-uniform", TestSubcase::Create)); addChild(new TestSubcase(m_context, "negative-bind", TestSubcase::Create)); addChild(new TestSubcase(m_context, "negative-compileErrors", TestSubcase::Create)); addChild(new TestSubcase(m_context, "negative-linkErrors", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-limits", TestSubcase::Create)); } }