blob: 1dd33848672c676d65c4a74431ff514ea7e31868 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
#include "gl4cShaderImageLoadStoreTests.hpp"
#include "gluContextInfo.hpp"
#include "glwEnums.hpp"
#include "tcuMatrix.hpp"
#include "tcuPlatform.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuVectorUtil.hpp"
#include <assert.h>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <deque>
#include <iomanip>
#include <map>
#include <sstream>
#include <tcuFloat.hpp>
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 <class T>
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<float>(1 << pixelFormat.redBits), 1.f / static_cast<float>(1 << pixelFormat.greenBits),
1.f / static_cast<float>(1 << pixelFormat.blueBits), 1.f / static_cast<float>(1 << pixelFormat.alphaBits));
std::vector<vec4> 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<GLushort> index_data(4);
for (int i = 0; i < 4; ++i)
{
index_data[i] = static_cast<GLushort>(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<void*>(sizeof(float) * 2));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 11, reinterpret_cast<void*>(sizeof(float) * 5));
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 11, reinterpret_cast<void*>(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 <typename T>
GLenum Format();
template <typename T>
GLenum Type();
template <typename T>
std::string TypePrefix();
template <typename T>
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<std::string, GLuint>& name_index_map,
GLint size, GLenum type)
{
std::map<std::string, GLuint>::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<GLsizei>(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<GLint>(f) < min_value)
return false;
glGetDoublev(pname, &d);
if (static_cast<GLint>(d) < min_value)
return false;
glGetInteger64v(pname, &i64);
if (static_cast<GLint>(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<GLuint>(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<GLenum>(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<GLenum>(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<vec4>()
{
return GL_RGBA;
}
template <>
GLenum ShaderImageLoadStoreBase::Format<ivec4>()
{
return GL_RGBA_INTEGER;
}
template <>
GLenum ShaderImageLoadStoreBase::Format<uvec4>()
{
return GL_RGBA_INTEGER;
}
template <>
GLenum ShaderImageLoadStoreBase::Format<GLint>()
{
return GL_RED_INTEGER;
}
template <>
GLenum ShaderImageLoadStoreBase::Format<GLuint>()
{
return GL_RED_INTEGER;
}
template <>
GLenum ShaderImageLoadStoreBase::Type<vec4>()
{
return GL_FLOAT;
}
template <>
GLenum ShaderImageLoadStoreBase::Type<ivec4>()
{
return GL_INT;
}
template <>
GLenum ShaderImageLoadStoreBase::Type<uvec4>()
{
return GL_UNSIGNED_INT;
}
template <>
GLenum ShaderImageLoadStoreBase::Type<GLint>()
{
return GL_INT;
}
template <>
GLenum ShaderImageLoadStoreBase::Type<GLuint>()
{
return GL_UNSIGNED_INT;
}
template <>
std::string ShaderImageLoadStoreBase::TypePrefix<vec4>()
{
return "";
}
template <>
std::string ShaderImageLoadStoreBase::TypePrefix<ivec4>()
{
return "i";
}
template <>
std::string ShaderImageLoadStoreBase::TypePrefix<uvec4>()
{
return "u";
}
template <>
std::string ShaderImageLoadStoreBase::TypePrefix<GLint>()
{
return "i";
}
template <>
std::string ShaderImageLoadStoreBase::TypePrefix<GLuint>()
{
return "u";
}
template <>
GLenum ShaderImageLoadStoreBase::ImageType<vec4>(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<ivec4>(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<uvec4>(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<GLenum>(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 <typename T>
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<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<GLint>(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 <typename T>
std::string GenFS(GLenum internalformat, const T& value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL
" imageStore(g_image, coord, "
<< TypePrefix<T>() << "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 <typename T>
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<T> 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<T>(), Type<T>(), &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<GLint>(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 <typename T>
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<T>()
<< "image2D g_image;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL " "
<< TypePrefix<T>() << "vec4 v = imageLoad(g_image, coord);" NL " if (v != " << TypePrefix<T>() << "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 <typename T>
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<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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 <typename T>
std::string GenVS(GLenum internalformat, const T& value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2DArray g_image0;" NL "void main() {" NL
" ivec3 coord = ivec3(gl_VertexID, 0, 0);" NL " imageStore(g_image0, coord, "
<< TypePrefix<T>() << "vec4" << value << ");" NL "}";
return os.str();
}
template <typename T>
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<T>()
<< "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<T>() << "vec4" << value << ");" NL "}";
return os.str();
}
template <typename T>
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<T>()
<< "image2DArray g_image2;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveID, 0, 0);" NL
" imageStore(g_image2, coord, "
<< TypePrefix<T>() << "vec4" << value << ");" NL "}";
return os.str();
}
template <typename T>
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<T>()
<< "image2DArray g_image3;" NL "void main() {" NL " ivec3 coord = ivec3(gl_PrimitiveIDIn, 0, 0);" NL
" imageStore(g_image3, coord, "
<< TypePrefix<T>() << "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 <typename T>
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<T> 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<T>(), Type<T>(), &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<float>(1 << pixelFormat.redBits), 1.f / static_cast<float>(1 << pixelFormat.greenBits),
1.f / static_cast<float>(1 << pixelFormat.blueBits), 1.f / static_cast<float>(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 <typename T>
std::string GenVS(GLenum internalformat, const T& expected_value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>()
<< "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<T>() << "vec4 v = imageLoad(g_image0, coord);" NL " if (v != " << TypePrefix<T>() << "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 <typename T>
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<T>()
<< "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<T>() << "vec4 v = imageLoad(g_image1, coord);" NL " if (v != " << TypePrefix<T>() << "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 <typename T>
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<T>()
<< "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<T>() << "vec4 v = imageLoad(g_image2, coord);" NL " if (v != " << TypePrefix<T>() << "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 <typename T>
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<T>()
<< "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<T>() << "vec4 v = imageLoad(g_image3, coord);" NL " if (v != " << TypePrefix<T>() << "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 <typename T>
bool Read(GLenum internalformat, const T& value, const T& expected_value)
{
GLuint program;
std::string source = GenCS<T>(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<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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 <typename T>
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<T>()
<< "image2D g_image_read;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2D g_image_write;" NL "void main() {" NL
" ivec2 coord = ivec2(int(gl_GlobalInvocationID.x), 0);" NL " "
<< TypePrefix<T>()
<< "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 <typename T>
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<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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 <typename T>
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 <typename T>
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<T> 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<T>(), Type<T>(),
&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<T>(), Type<T>(), &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 <typename T>
std::string GenFS(GLenum internalformat, const T& write_value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>() << "image2D g_image_2d;" NL "layout("
<< FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix<T>()
<< "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>() << "imageCube g_image_cube;" NL "layout("
<< FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix<T>()
<< "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>()
<< "image2DArray g_image_2darray;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL
" imageStore(g_image_1d, coord.x, "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_2d, coord, " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " imageStore(g_image_3d, ivec3(coord.xy, 0), " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " imageStore(g_image_3d, ivec3(coord.xy, 1), " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " imageStore(g_image_2drect, coord, " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 0), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 1), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 2), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 3), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 4), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_cube, ivec3(coord, 5), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_buffer, coord.x, " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_1darray, ivec2(coord.x, 0), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_1darray, ivec2(coord.x, 1), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_2darray, ivec3(coord, 0), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_2darray, ivec3(coord, 1), " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " discard;" NL "}";
return os.str();
}
template <typename T>
std::string GenFSMS(GLenum internalformat, const T& write_value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>()
<< "image2DMSArray g_image_2dms_array;" NL "void main() {" NL " ivec2 coord = ivec2(gl_FragCoord.xy);" NL
" imageStore(g_image_2dms, coord, 0, "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 1, "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 2, "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_2dms, coord, 3, "
<< TypePrefix<T>() << "vec4" << write_value
<< ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 0, " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 1, " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 2, "
<< TypePrefix<T>() << "vec4" << write_value
<< ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 0), 3, " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 0, " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 1, "
<< TypePrefix<T>() << "vec4" << write_value
<< ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 2, " << TypePrefix<T>() << "vec4"
<< write_value << ");" NL " imageStore(g_image_2dms_array, ivec3(coord, 1), 3, " << TypePrefix<T>()
<< "vec4" << write_value << ");" NL " discard;" NL "}";
return os.str();
}
template <typename T>
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<T>()
<< "sampler2DMS g_sampler_2dms;" NL "uniform " << TypePrefix<T>()
<< "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<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 1) != "
<< TypePrefix<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 2) != "
<< TypePrefix<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (texelFetch(g_sampler_2dms, coord, 3) != "
<< TypePrefix<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.3);" NL "}";
return os.str();
}
template <typename T>
std::string GenFSCubeArray(GLenum internalformat, const T& write_value)
{
std::ostringstream os;
os << "#version 420 core" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>()
<< "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<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 1), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 2), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 3), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 4), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 5), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 6), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 7), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 8), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 9), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 10), "
<< TypePrefix<T>() << "vec4" << write_value << ");" NL " imageStore(g_image_cube_array, ivec3(coord, 11), "
<< TypePrefix<T>() << "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 <typename T>
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<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<std::string, GLuint> 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<GLuint>(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<T>(GL_TEXTURE_1D)))
status = false;
if (!CheckUniform(program, "g_image_2d", name_index_map, 1, ImageType<T>(GL_TEXTURE_2D)))
status = false;
if (!CheckUniform(program, "g_image_3d", name_index_map, 1, ImageType<T>(GL_TEXTURE_3D)))
status = false;
if (!CheckUniform(program, "g_image_2drect", name_index_map, 1, ImageType<T>(GL_TEXTURE_RECTANGLE)))
status = false;
if (!CheckUniform(program, "g_image_buffer", name_index_map, 1, ImageType<T>(GL_TEXTURE_BUFFER)))
status = false;
if (!CheckUniform(program, "g_image_1darray", name_index_map, 1, ImageType<T>(GL_TEXTURE_1D_ARRAY)))
status = false;
if (!CheckUniform(program, "g_image_2darray", name_index_map, 1, ImageType<T>(GL_TEXTURE_2D_ARRAY)))
status = false;
glUseProgram(0);
glDeleteProgram(program);
glDeleteTextures(7, textures);
glDeleteBuffers(1, &buffer);
return status;
}
template <typename T>
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<T> 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<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&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<T>(), Type<T>(),
&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<std::string, GLuint> 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<GLuint>(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<T>(GL_TEXTURE_CUBE_MAP)))
status = false;
if (!CheckUniform(program, "g_image_cube_array", name_index_map, 1, ImageType<T>(GL_TEXTURE_CUBE_MAP_ARRAY)))
status = false;
glUseProgram(0);
glDeleteProgram(program);
glDeleteTextures(2, textures);
return status;
}
template <typename T>
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<T>()
<< "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") readonly uniform " << TypePrefix<T>() << "image3D g_image_3d;" NL "layout("
<< FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix<T>()
<< "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat)
<< ") readonly uniform " << TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout("
<< FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix<T>()
<< "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<T>()
<< "vec4 v;" NL " v = imageLoad(g_image_1d, coord.x);" NL " if (v != " << TypePrefix<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "vec4" << expected_value << ") o_color = vec4(1.0, 0.0, 0.0, 0.8);" NL "}";
return os.str();
}
template <typename T>
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<T>()
<< "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>()
<< "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<T>()
<< "vec4 v;" NL " v = imageLoad(g_image_cube, ivec3(coord, 0));" NL " if (v != " << TypePrefix<T>()
<< "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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 <typename T>
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<std::string, GLuint> 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<GLuint>(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<T>(GL_TEXTURE_2D_MULTISAMPLE)))
status = false;
if (!CheckUniform(program, "g_image_2dms_array", name_index_map, 1,
ImageType<T>(GL_TEXTURE_2D_MULTISAMPLE_ARRAY)))
status = false;
glDeleteTextures(2, textures);
glUseProgram(0);
glDeleteProgram(program);
return status;
}
template <typename T>
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<T>()
<< "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>()
<< "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<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 1) != "
<< TypePrefix<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 2) != "
<< TypePrefix<T>() << "vec4" << expected_value
<< ") o_color = vec4(1.0, 0.0, 0.0, 0.1);" NL " if (imageLoad(g_image_2dms, coord, 3) != "
<< TypePrefix<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<T>() << "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<GLint>(GL_R32I))
return ERROR;
if (!Atomic<GLuint>(GL_R32UI))
return ERROR;
if (!AtomicCube<GLint>(GL_R32I))
return ERROR;
if (!AtomicCube<GLuint>(GL_R32UI))
return ERROR;
GLint isamples;
glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &isamples);
if (SupportedSamples(4) && isamples >= 4)
{
if (!AtomicMS<GLint>(GL_R32I))
return ERROR;
if (!AtomicMS<GLuint>(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 <typename T>
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<T>(internalformat).c_str());
GLuint textures[7];
GLuint buffer;
glGenTextures(7, textures);
glGenBuffers(1, &buffer);
const int kSize = 16;
std::vector<T> 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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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<T>(), Type<T>(), &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 <typename T>
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<T>(internalformat).c_str());
GLuint textures[2];
glGenTextures(2, textures);
const int kSize = 16;
std::vector<T> 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<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&data[0]);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, kSize, kSize, 0, Format<T>(), Type<T>(),
&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<T>(), Type<T>(),
&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 <typename T>
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<T>(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 <typename T>
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<T>()
<< "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") coherent uniform " << TypePrefix<T>() << "image3D g_image_3d;" NL "layout("
<< FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix<T>()
<< "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat)
<< ") coherent uniform " << TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout("
<< FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix<T>()
<< "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 <typename T>
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<T>()
<< "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>()
<< "imageCube 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 <typename T>
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<T>()
<< "image2DMS g_image_2dms;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>()
<< "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 <typename T>
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<T>(), Type<T>(), &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<T>(), Type<T>(), &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 <typename T>
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<T>()
<< "image1D g_image_1d;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>() << "image3D g_image_3d;" NL "layout("
<< FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix<T>()
<< "image2DRect g_image_2drect;" NL "layout(" << FormatEnumToString(internalformat) << ") writeonly uniform "
<< TypePrefix<T>() << "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout("
<< FormatEnumToString(internalformat) << ") writeonly uniform " << TypePrefix<T>()
<< "image2DArray g_image_2darray;" NL "layout(" << FormatEnumToString(internalformat)
<< ") writeonly uniform " << TypePrefix<T>() << "imageCubeArray g_image_cube_array;" NL "uniform "
<< TypePrefix<T>() << "vec4 g_value = " << TypePrefix<T>() << "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 <typename T>
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<T>()
<< "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") readonly uniform " << TypePrefix<T>() << "image2DRect g_image_2drect;" NL "layout("
<< FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix<T>()
<< "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") readonly uniform "
<< TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout(" << FormatEnumToString(internalformat)
<< ") readonly uniform " << TypePrefix<T>() << "image2DArray g_image_2darray;" NL "layout("
<< FormatEnumToString(internalformat) << ") readonly uniform " << TypePrefix<T>()
<< "imageCubeArray g_image_cube_array;" NL "layout(rgba32f) writeonly uniform image2D g_image_result;" NL
"uniform "
<< TypePrefix<T>() << "vec4 g_value = " << TypePrefix<T>() << "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<T>()
<< "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 <typename T>
bool Atomic(int stage, GLenum internalformat)
{
GLuint program = 0;
if (stage == 0)
{ // VS
program = BuildProgram(GenShader<T>(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<T>(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<T>(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<T>(stage, internalformat).c_str(), NULL);
}
else if (stage == 4)
{ // CS
std::string source = GenShader<T>(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 <typename T>
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<T>()
<< "image2D g_image_2d;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>() << "image3D g_image_3d;" NL "layout(" << FormatEnumToString(internalformat)
<< ") coherent uniform " << TypePrefix<T>() << "image2DRect g_image_2drect;" NL "layout("
<< FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix<T>()
<< "imageCube g_image_cube;" NL "layout(" << FormatEnumToString(internalformat) << ") coherent uniform "
<< TypePrefix<T>() << "imageBuffer g_image_buffer;" NL "layout(" << FormatEnumToString(internalformat)
<< ") coherent uniform " << TypePrefix<T>() << "image1DArray g_image_1darray;" NL "layout("
<< FormatEnumToString(internalformat) << ") coherent uniform " << TypePrefix<T>()
<< "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<GLint>(stage, GL_R32I))
return ERROR;
if (!Atomic<GLuint>(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<vec4> 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<vec4> 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<vec4> 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<vec4*>(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<float>(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<GLuint>(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<vec4> 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<vec4> 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<vec4> 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<vec4> 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<vec4> 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<vec4> 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<GLubyte> 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<GLubyte> 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<TextureDimensions> 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<GLubyte> left_texture_data;
std::vector<GLubyte> 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<GLubyte> 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<GLubyte> 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<GLubyte>& 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 <layered>: 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<TextureShapeDefinition> 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<GLubyte> left_texture_data;
std::vector<GLubyte> 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<GLubyte> 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<GLubyte> 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<GLubyte>(layer);
for (GLuint y = 0; y < edge; ++y)
{
const GLuint line_offset = y * edge * n_components + layer_offset;
texel[1] = static_cast<GLubyte>(y);
for (GLuint x = 0; x < edge; ++x)
{
const GLuint texel_offset = x * n_components + line_offset;
texel[0] = static_cast<GLubyte>(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<GLubyte>& 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<GLubyte>& 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 <level>: 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 <level>: 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<GLubyte> black_texture_data;
std::vector<GLubyte> 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<GLubyte> expected_texture_data;
std::vector<GLubyte> 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<GLubyte>(x), static_cast<GLubyte>(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<GLubyte> 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<GLubyte>(x), static_cast<GLubyte>(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<GLubyte> 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<GLubyte>(x), static_cast<GLubyte>(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 <format> is selected image format, <gimage2D> is type of 2D image
* compatible with <format> and <u_image> 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 <typename T, GLuint SIZE, GLuint OFFSET, bool = (OFFSET < sizeof(T) * CHAR_BIT)>
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 <typename T, GLuint SIZE, GLuint OFFSET>
struct Masks<T, SIZE, OFFSET, false>
{
/** 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 <typename T, GLuint SIZE, GLuint OFFSET>
class Integer
{
public:
/** Constructor
*
**/
Integer(T raw) : m_raw(raw)
{
}
/** Extract value from bit-field
*
* @return Value
**/
T Get() const
{
const T mask = Masks<T, SIZE, OFFSET>::RawMask();
const T bits = m_raw & mask;
const T result = (unsigned)bits >> Masks<T, SIZE, OFFSET>::Offset();
return result;
}
/** Extract value from bit-field and negate it
*
* @return Negated value
**/
T GetNegated() const
{
const T mask = Masks<T, SIZE, OFFSET>::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<GLushort> },
{ "r11f_g11f_b10f", "image2D", "vec4", GL_R11F_G11F_B10F,
ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned<GLuint> },
{ "r32f", "image2D", "vec4", GL_R32F, ImageLoadStoreMultipleUniformsTest::verifyFloat<GLuint> },
{ "rgb10_a2", "image2D", "vec4", GL_RGB10_A2,
ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned<GLuint> },
{ "rgba8", "image2D", "vec4", GL_RGBA8, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned<GLuint> },
{ "rg16", "image2D", "vec4", GL_RG16, ImageLoadStoreMultipleUniformsTest::verifyFloatUnsigned<GLuint> },
{ "rgba8_snorm", "image2D", "vec4", GL_RGBA8_SNORM,
ImageLoadStoreMultipleUniformsTest::verifyFloatSignedNorm<GLbyte> },
{ "rg16_snorm", "image2D", "vec4", GL_RG16_SNORM,
ImageLoadStoreMultipleUniformsTest::verifyFloatSignedNorm<GLshort> },
{ "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<GLint> 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<GLint> 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 <typename T>
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 <typename T>
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 <GLuint R, GLuint G, GLuint B, GLuint A, typename T>
static bool verifyInteger(GLint original_value, GLint positive_value, GLint negated_value)
{
if (original_value != positive_value)
{
return false;
}
Integer<T, R, 0> positive_red(positive_value);
Integer<T, R, 0> negated_red(negated_value);
Integer<T, G, R> positive_green(positive_value);
Integer<T, G, R> negated_green(negated_value);
Integer<T, B, R + G> positive_blue(positive_value);
Integer<T, B, R + G> negated_blue(negated_value);
Integer<T, A, R + G + B> positive_alpha(positive_value);
Integer<T, A, R + G + B> 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 <typename T>
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:
* - <func> to GL_LESS,
* - <ref> to 128,
* - <mask> 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:
* - <func> to GL_LESS,
* - <ref> to 0,
* - <mask> 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:
* - <func> to GL_LESS,
* - <ref> to 128,
* - <mask> 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<GLfloat> m_clean_texture_data;
std::vector<GLfloat> 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 <value> 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 <value> 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 <value> 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 <value> 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 <value> 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 <value> 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 <value> 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 <value> "
"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 <unit> 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 <texture> 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 <format> 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<BasicAPIGet>));
addChild(new TestSubcase(m_context, "basic-api-bind", TestSubcase::Create<BasicAPIBind>));
addChild(new TestSubcase(m_context, "basic-api-barrier", TestSubcase::Create<BasicAPIBarrier>));
addChild(new TestSubcase(m_context, "basic-api-texParam", TestSubcase::Create<BasicAPITexParam>));
addChild(new TestSubcase(m_context, "basic-allFormats-store", TestSubcase::Create<BasicAllFormatsStore>));
addChild(new TestSubcase(m_context, "basic-allFormats-load", TestSubcase::Create<BasicAllFormatsLoad>));
addChild(new TestSubcase(m_context, "basic-allFormats-storeGeometryStages",
TestSubcase::Create<BasicAllFormatsStoreGeometryStages>));
addChild(new TestSubcase(m_context, "basic-allFormats-loadGeometryStages",
TestSubcase::Create<BasicAllFormatsLoadGeometryStages>));
addChild(new TestSubcase(m_context, "basic-allFormats-loadStoreComputeStage",
TestSubcase::Create<BasicAllFormatsLoadStoreComputeStage>));
addChild(new TestSubcase(m_context, "basic-allTargets-store", TestSubcase::Create<BasicAllTargetsStore>));
addChild(new TestSubcase(m_context, "basic-allTargets-load-nonMS", TestSubcase::Create<BasicAllTargetsLoadNonMS>));
addChild(new TestSubcase(m_context, "basic-allTargets-load-ms", TestSubcase::Create<BasicAllTargetsLoadMS>));
addChild(new TestSubcase(m_context, "basic-allTargets-atomic", TestSubcase::Create<BasicAllTargetsAtomic>));
addChild(
new TestSubcase(m_context, "basic-allTargets-loadStoreVS", TestSubcase::Create<BasicAllTargetsLoadStoreVS>));
addChild(
new TestSubcase(m_context, "basic-allTargets-loadStoreTCS", TestSubcase::Create<BasicAllTargetsLoadStoreTCS>));
addChild(
new TestSubcase(m_context, "basic-allTargets-loadStoreTES", TestSubcase::Create<BasicAllTargetsLoadStoreTES>));
addChild(
new TestSubcase(m_context, "basic-allTargets-loadStoreGS", TestSubcase::Create<BasicAllTargetsLoadStoreGS>));
addChild(
new TestSubcase(m_context, "basic-allTargets-loadStoreCS", TestSubcase::Create<BasicAllTargetsLoadStoreCS>));
addChild(new TestSubcase(m_context, "basic-allTargets-atomicVS", TestSubcase::Create<BasicAllTargetsAtomicVS>));
addChild(new TestSubcase(m_context, "basic-allTargets-atomicTCS", TestSubcase::Create<BasicAllTargetsAtomicTCS>));
addChild(new TestSubcase(m_context, "basic-allTargets-atomicGS", TestSubcase::Create<BasicAllTargetsAtomicGS>));
addChild(new TestSubcase(m_context, "basic-allTargets-atomicCS", TestSubcase::Create<BasicAllTargetsAtomicCS>));
addChild(new TestSubcase(m_context, "basic-glsl-misc", TestSubcase::Create<BasicGLSLMisc>));
addChild(new TestSubcase(m_context, "basic-glsl-earlyFragTests", TestSubcase::Create<BasicGLSLEarlyFragTests>));
addChild(new TestSubcase(m_context, "basic-glsl-const", TestSubcase::Create<BasicGLSLConst>));
addChild(new TestSubcase(m_context, "advanced-sync-imageAccess", TestSubcase::Create<AdvancedSyncImageAccess>));
addChild(new TestSubcase(m_context, "advanced-sync-vertexArray", TestSubcase::Create<AdvancedSyncVertexArray>));
addChild(new TestSubcase(m_context, "advanced-sync-drawIndirect", TestSubcase::Create<AdvancedSyncDrawIndirect>));
addChild(new TestSubcase(m_context, "advanced-sync-textureUpdate", TestSubcase::Create<AdvancedSyncTextureUpdate>));
addChild(new TestSubcase(m_context, "advanced-sync-imageAccess2", TestSubcase::Create<AdvancedSyncImageAccess2>));
addChild(new TestSubcase(m_context, "advanced-sync-bufferUpdate", TestSubcase::Create<AdvancedSyncBufferUpdate>));
addChild(new TestSubcase(m_context, "advanced-allStages-oneImage", TestSubcase::Create<AdvancedAllStagesOneImage>));
addChild(new TestSubcase(m_context, "advanced-memory-dependentInvocation",
TestSubcase::Create<AdvancedMemoryDependentInvocation>));
addChild(new TestSubcase(m_context, "advanced-memory-order", TestSubcase::Create<AdvancedMemoryOrder>));
addChild(new TestSubcase(m_context, "advanced-sso-simple", TestSubcase::Create<AdvancedSSOSimple>));
addChild(new TestSubcase(m_context, "advanced-sso-atomicCounters", TestSubcase::Create<AdvancedSSOAtomicCounters>));
addChild(new TestSubcase(m_context, "advanced-sso-subroutine", TestSubcase::Create<AdvancedSSOSubroutine>));
addChild(new TestSubcase(m_context, "advanced-sso-perSample", TestSubcase::Create<AdvancedSSOPerSample>));
addChild(new TestSubcase(m_context, "advanced-copyImage", TestSubcase::Create<AdvancedCopyImage>));
addChild(new TestSubcase(m_context, "advanced-allMips", TestSubcase::Create<AdvancedAllMips>));
addChild(new TestSubcase(m_context, "advanced-cast", TestSubcase::Create<AdvancedCast>));
addChild(
new TestSubcase(m_context, "single-byte_data_alignment", TestSubcase::Create<ImageLoadStoreDataAlignmentTest>));
addChild(
new TestSubcase(m_context, "non-layered_binding", TestSubcase::Create<ImageLoadStoreNonLayeredBindingTest>));
addChild(
new TestSubcase(m_context, "incomplete_textures", TestSubcase::Create<ImageLoadStoreIncompleteTexturesTest>));
addChild(new TestSubcase(m_context, "multiple-uniforms", TestSubcase::Create<ImageLoadStoreMultipleUniformsTest>));
addChild(
new TestSubcase(m_context, "early-fragment-tests", TestSubcase::Create<ImageLoadStoreEarlyFragmentTestsTest>));
addChild(new TestSubcase(m_context, "negative-uniform", TestSubcase::Create<NegativeUniform>));
addChild(new TestSubcase(m_context, "negative-bind", TestSubcase::Create<NegativeBind>));
addChild(new TestSubcase(m_context, "negative-compileErrors", TestSubcase::Create<NegativeCompileErrors>));
addChild(new TestSubcase(m_context, "negative-linkErrors", TestSubcase::Create<NegativeLinkErrors>));
addChild(new TestSubcase(m_context, "uniform-limits", TestSubcase::Create<ImageLoadStoreUniformLimitsTest>));
}
}