blob: d3dbcf8e4d5f1231c8fb7314a843c4028f868e64 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014 Intel Corporation
* Copyright (c) 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 "glcShaderIntegerMixTests.hpp"
#include "deMath.h"
#include "deRandom.hpp"
#include "deString.h"
#include "deStringUtil.hpp"
#include "gluContextInfo.hpp"
#include "gluDrawUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluShaderProgram.hpp"
#include "glw.h"
#include "glwFunctions.hpp"
#include "tcuCommandLine.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
namespace deqp
{
using tcu::TestLog;
class ShaderIntegerMixCase : public TestCase
{
public:
ShaderIntegerMixCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion)
: TestCase(context, name, description), m_glslVersion(glslVersion)
{
}
~ShaderIntegerMixCase()
{
// empty
}
IterateResult iterate()
{
qpTestResult result = test();
m_testCtx.setTestResult(result, qpGetTestResultName(result));
return STOP;
}
protected:
glu::GLSLVersion m_glslVersion;
virtual qpTestResult test() = 0;
};
class ShaderIntegerMixDefineCase : public ShaderIntegerMixCase
{
public:
ShaderIntegerMixDefineCase(Context& context, const char* name, const char* description,
glu::GLSLVersion glslVersion)
: ShaderIntegerMixCase(context, name, description, glslVersion)
{
// empty
}
~ShaderIntegerMixDefineCase()
{
// empty
}
protected:
virtual qpTestResult test()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool pass = true;
static const char source_template[] = "${VERSION_DECL}\n"
"#extension GL_EXT_shader_integer_mix: require\n"
"\n"
"#if !defined GL_EXT_shader_integer_mix\n"
"# error GL_EXT_shader_integer_mix is not defined\n"
"#elif GL_EXT_shader_integer_mix != 1\n"
"# error GL_EXT_shader_integer_mix is not equal to 1\n"
"#endif\n"
"\n"
"void main(void) { ${BODY} }\n";
static const struct
{
GLenum target;
const char* body;
} shader_targets[] = {
{ GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" },
};
const glu::GLSLVersion v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
return QP_TEST_RESULT_NOT_SUPPORTED;
for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++)
{
std::map<std::string, std::string> args;
args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(v);
args["BODY"] = shader_targets[i].body;
std::string code = tcu::StringTemplate(source_template).specialize(args);
GLuint shader = gl.createShader(shader_targets[i].target);
char const* strings[1] = { code.c_str() };
gl.shaderSource(shader, 1, strings, 0);
gl.compileShader(shader);
GLint compileSuccess = 0;
gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
gl.deleteShader(shader);
if (!compileSuccess)
pass = false;
}
return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
}
};
class ShaderIntegerMixPrototypesCase : public ShaderIntegerMixCase
{
public:
ShaderIntegerMixPrototypesCase(Context& context, const char* name, const char* description,
glu::GLSLVersion glslVersion, bool _use_extension, bool _is_negative_testing)
: ShaderIntegerMixCase(context, name, description, glslVersion)
, use_extension(_use_extension)
, is_negative_testing(_is_negative_testing)
{
// empty
}
~ShaderIntegerMixPrototypesCase()
{
// empty
}
protected:
bool use_extension;
bool is_negative_testing;
virtual qpTestResult test()
{
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool pass = true;
static const char source_template[] = "${VERSION_DECL}\n"
"${EXTENSION_ENABLE}\n"
"\n"
"void main()\n"
"{\n"
" mix(ivec2(1), ivec2(2), bvec2(true));\n"
" mix(ivec3(1), ivec3(2), bvec3(true));\n"
" mix(ivec4(1), ivec4(2), bvec4(true));\n"
" mix(uvec2(1), uvec2(2), bvec2(true));\n"
" mix(uvec3(1), uvec3(2), bvec3(true));\n"
" mix(uvec4(1), uvec4(2), bvec4(true));\n"
" mix(bvec2(1), bvec2(0), bvec2(true));\n"
" mix(bvec3(1), bvec3(0), bvec3(true));\n"
" mix(bvec4(1), bvec4(0), bvec4(true));\n"
" ${BODY}\n"
"}\n";
static const struct
{
GLenum target;
const char* body;
} shader_targets[] = {
{ GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" },
};
glu::GLSLVersion v;
const char* extension_enable;
if (use_extension)
{
v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
extension_enable = "#extension GL_EXT_shader_integer_mix: enable";
if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
return QP_TEST_RESULT_NOT_SUPPORTED;
}
else if (is_negative_testing)
{
v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
extension_enable = "";
}
else
{
v = m_glslVersion;
extension_enable = "";
if (glslVersionIsES(m_glslVersion))
{
if (m_glslVersion < glu::GLSL_VERSION_310_ES)
return QP_TEST_RESULT_NOT_SUPPORTED;
}
else
{
if (m_glslVersion < glu::GLSL_VERSION_450)
return QP_TEST_RESULT_NOT_SUPPORTED;
}
}
for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++)
{
std::map<std::string, std::string> args;
args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(v);
args["EXTENSION_ENABLE"] = extension_enable;
args["BODY"] = shader_targets[i].body;
std::string code = tcu::StringTemplate(source_template).specialize(args);
GLuint shader = gl.createShader(shader_targets[i].target);
char const* strings[1] = { code.c_str() };
gl.shaderSource(shader, 1, strings, 0);
gl.compileShader(shader);
GLint compileSuccess = 0;
gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
if (is_negative_testing)
{
if (compileSuccess)
{
TCU_FAIL("The shader compilation was expected to fail, but it was successful.");
pass = false;
}
}
else if (!compileSuccess)
{
GLchar infoLog[1000];
gl.getShaderInfoLog(shader, sizeof(infoLog), NULL, infoLog);
log.writeKernelSource(strings[0]);
log.writeCompileInfo("shader", "", false, infoLog);
pass = false;
}
gl.deleteShader(shader);
}
return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
}
};
class ShaderIntegerMixRenderCase : public ShaderIntegerMixCase
{
public:
ShaderIntegerMixRenderCase(Context& context, const char* name, const char* description,
glu::GLSLVersion glslVersion, const char* _type)
: ShaderIntegerMixCase(context, name, description, glslVersion), type(_type)
{
// empty
}
~ShaderIntegerMixRenderCase()
{
// empty
}
protected:
// Type used for mix() parameters in this test case.
const char* type;
static const unsigned width = 8 * 8;
static const unsigned height = 8 * 8;
virtual qpTestResult test()
{
static const char vs_template[] = "${VERSION_DECL}\n"
"${EXTENSION_ENABLE}\n"
"\n"
"in vec2 vertex;\n"
"in ivec4 vs_in_a;\n"
"in ivec4 vs_in_b;\n"
"in ivec4 vs_in_sel;\n"
"\n"
"flat out ivec4 fs_in_a;\n"
"flat out ivec4 fs_in_b;\n"
"flat out ivec4 fs_in_sel;\n"
"flat out ivec4 fs_in_result;\n"
"\n"
"void main()\n"
"{\n"
" fs_in_a = vs_in_a;\n"
" fs_in_b = vs_in_b;\n"
" fs_in_sel = vs_in_sel;\n"
"\n"
" ${TYPE} a = ${TYPE}(vs_in_a);\n"
" ${TYPE} b = ${TYPE}(vs_in_b);\n"
" bvec4 sel = bvec4(vs_in_sel);\n"
" fs_in_result = ivec4(mix(a, b, sel));\n"
"\n"
" gl_Position = vec4(vertex, 0, 1);\n"
" gl_PointSize = 4.;\n"
"}\n";
static const char fs_template[] = "${VERSION_DECL}\n"
"${EXTENSION_ENABLE}\n"
"\n"
"out ivec4 o;\n"
"\n"
"flat in ivec4 fs_in_a;\n"
"flat in ivec4 fs_in_b;\n"
"flat in ivec4 fs_in_sel;\n"
"flat in ivec4 fs_in_result;\n"
"\n"
"uniform bool use_vs_data;\n"
"\n"
"void main()\n"
"{\n"
" if (use_vs_data)\n"
" o = fs_in_result;\n"
" else {\n"
" ${TYPE} a = ${TYPE}(fs_in_a);\n"
" ${TYPE} b = ${TYPE}(fs_in_b);\n"
" bvec4 sel = bvec4(fs_in_sel);\n"
" o = ivec4(mix(a, b, sel));\n"
" }\n"
"}\n";
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool pass = true;
const char* extension_enable;
bool is_es = glslVersionIsES(m_glslVersion);
if ((is_es && (m_glslVersion < glu::GLSL_VERSION_310_ES)) || !is_es)
{
/* For versions that do not support this feature in Core it must be exposed via an extension. */
if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
{
return QP_TEST_RESULT_NOT_SUPPORTED;
}
extension_enable = "#extension GL_EXT_shader_integer_mix: enable";
}
else
{
extension_enable = "";
}
/* Generate the specialization of the shader for the specific
* type being tested.
*/
std::map<std::string, std::string> args;
args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(m_glslVersion);
args["EXTENSION_ENABLE"] = extension_enable;
args["TYPE"] = type;
std::string vs_code = tcu::StringTemplate(vs_template).specialize(args);
std::string fs_code = tcu::StringTemplate(fs_template).specialize(args);
glu::ShaderProgram prog(m_context.getRenderContext(),
glu::makeVtxFragSources(vs_code.c_str(), fs_code.c_str()));
if (!prog.isOk())
{
log << prog;
TCU_FAIL("Compile failed");
}
if (!glslVersionIsES(m_glslVersion))
glEnable(GL_PROGRAM_POINT_SIZE);
/* Generate an integer FBO for rendering.
*/
GLuint fbo;
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA32I, width, height, 0 /* border */, GL_RGBA_INTEGER, GL_INT,
NULL /* data */);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "Creation of rendering FBO failed.");
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
TCU_FAIL("Framebuffer not complete.");
glViewport(0, 0, width, height);
/* Fill a VBO with some vertex data.
*/
deUint32 pointIndices[256];
float vertex[DE_LENGTH_OF_ARRAY(pointIndices) * 2];
deInt32 a[DE_LENGTH_OF_ARRAY(pointIndices) * 4];
deInt32 b[DE_LENGTH_OF_ARRAY(a)];
deInt32 sel[DE_LENGTH_OF_ARRAY(a)];
tcu::IVec4 expected[DE_LENGTH_OF_ARRAY(pointIndices)];
for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
{
pointIndices[i] = deUint16(i);
const int x = (i / 16) * (width / 16) + (4 / 2);
const int y = (i % 16) * (height / 16) + (4 / 2);
vertex[(i * 2) + 0] = float(x) * 2.0f / float(width) - 1.0f;
vertex[(i * 2) + 1] = float(y) * 2.0f / float(height) - 1.0f;
a[(i * 4) + 0] = i;
a[(i * 4) + 1] = i * 5;
a[(i * 4) + 2] = i * 7;
a[(i * 4) + 3] = i * 11;
b[(i * 4) + 0] = ~a[(i * 4) + 3];
b[(i * 4) + 1] = ~a[(i * 4) + 2];
b[(i * 4) + 2] = ~a[(i * 4) + 1];
b[(i * 4) + 3] = ~a[(i * 4) + 0];
sel[(i * 4) + 0] = (i >> 0) & 1;
sel[(i * 4) + 1] = (i >> 1) & 1;
sel[(i * 4) + 2] = (i >> 2) & 1;
sel[(i * 4) + 3] = (i >> 3) & 1;
expected[i] = tcu::IVec4(
sel[(i * 4) + 0] ? b[(i * 4) + 0] : a[(i * 4) + 0], sel[(i * 4) + 1] ? b[(i * 4) + 1] : a[(i * 4) + 1],
sel[(i * 4) + 2] ? b[(i * 4) + 2] : a[(i * 4) + 2], sel[(i * 4) + 3] ? b[(i * 4) + 3] : a[(i * 4) + 3]);
}
/* Mask off all but the least significant bit for boolean
* types.
*/
if (type[0] == 'b')
{
for (int i = 0; i < DE_LENGTH_OF_ARRAY(a); i++)
{
a[i] &= 1;
b[i] &= 1;
expected[i / 4][0] &= 1;
expected[i / 4][1] &= 1;
expected[i / 4][2] &= 1;
expected[i / 4][3] &= 1;
}
}
glu::VertexArrayBinding vertexArrays[] = {
glu::va::Float("vertex", 2, DE_LENGTH_OF_ARRAY(pointIndices), 0, vertex),
glu::va::Int32("vs_in_a", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, a),
glu::va::Int32("vs_in_b", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, b),
glu::va::Int32("vs_in_sel", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, sel)
};
/* Render and verify the results. Rendering happens twice.
* The first time, use_vs_data is false, and the mix() result
* from the fragment shader is used. The second time,
* use_vs_data is true, and the mix() result from the vertex
* shader is used.
*/
const GLint loc = gl.getUniformLocation(prog.getProgram(), "use_vs_data");
gl.useProgram(prog.getProgram());
static const GLint clear[] = { 1, 2, 3, 4 };
glClearBufferiv(GL_COLOR, 0, clear);
gl.uniform1i(loc, 0);
glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices));
for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
{
const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f);
const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f);
pass = probe_pixel(log, "Fragment", x, y, expected[i]) && pass;
}
gl.uniform1i(loc, 1);
glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices));
for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
{
const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f);
const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f);
pass = probe_pixel(log, "Vertex", x, y, expected[i]) && pass;
}
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
}
bool probe_pixel(TestLog& log, const char* stage, int x, int y, const tcu::IVec4& expected)
{
tcu::IVec4 pixel;
glReadPixels(x, y, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixel);
if (expected != pixel)
{
log << TestLog::Message << stage << " shader failed at pixel (" << x << ", " << y << "). "
<< "Got " << pixel << ", expected " << expected << ")." << TestLog::EndMessage;
return false;
}
return true;
}
};
ShaderIntegerMixTests::ShaderIntegerMixTests(Context& context, glu::GLSLVersion glslVersion)
: TestCaseGroup(context, "shader_integer_mix", "Shader Integer Mix tests"), m_glslVersion(glslVersion)
{
// empty
}
ShaderIntegerMixTests::~ShaderIntegerMixTests()
{
// empty
}
void ShaderIntegerMixTests::init(void)
{
addChild(new ShaderIntegerMixDefineCase(m_context, "define", "Verify GL_EXT_shader_integer_mix is defined to 1.",
m_glslVersion));
addChild(new ShaderIntegerMixPrototypesCase(m_context, "prototypes-extension",
"Verify availability of all function signatures with the extension.",
m_glslVersion, true, false));
addChild(new ShaderIntegerMixPrototypesCase(
m_context, "prototypes", "Verify availability of all function signatures with the proper GLSL version.",
m_glslVersion, false, false));
addChild(new ShaderIntegerMixPrototypesCase(
m_context, "prototypes-negative",
"Verify compilation fails if the GLSL version does not support shader_integer_mix", m_glslVersion, false,
true));
static const char* types_to_test[] = { "ivec4", "uvec4", "bvec4" };
for (int i = 0; i < DE_LENGTH_OF_ARRAY(types_to_test); i++)
{
std::stringstream name;
name << "mix-" << types_to_test[i];
std::stringstream description;
description << "Verify functionality of mix() with " << types_to_test[i] << " parameters.";
addChild(new ShaderIntegerMixRenderCase(m_context, name.str().c_str(), description.str().c_str(), m_glslVersion,
types_to_test[i]));
}
}
} // namespace deqp