blob: d4c2ac74aa950ae43d15b2740b5cf3c7c7872f69 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2015-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 "gl4cStencilTexturingTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "gluStrUtil.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuTestLog.hpp"
#include <algorithm>
#include <string>
#include <vector>
#define DEBUG_REPLACE_TOKEN 0
using namespace glw;
namespace gl4cts
{
namespace StencilTexturing
{
class Utils
{
public:
static GLuint createAndBuildProgram(deqp::Context& context, const GLchar* cs_code, const GLchar* fs_code,
const GLchar* gs_code, const GLchar* tcs_code, const GLchar* tes_code,
const GLchar* vs_code);
static GLuint createAndCompileShader(deqp::Context& context, const GLenum type, const GLchar* code);
static GLuint createAndFill2DTexture(deqp::Context& context, GLuint width, GLuint height, GLenum internal_format,
GLenum format, GLenum type, const GLvoid* data);
static void deleteProgram(deqp::Context& context, const GLuint id);
static void deleteShader(deqp::Context& context, const GLuint id);
static void deleteTexture(deqp::Context& context, const GLuint id);
static bool isExtensionSupported(deqp::Context& context, const GLchar* extension_name);
static void replaceToken(const GLchar* token, size_t& search_position, const GLchar* text, std::string& string);
};
/** Create and build program from provided sources
*
* @param context Test context
* @param cs_code Source code for compute shader stage
* @param fs_code Source code for fragment shader stage
* @param gs_code Source code for geometry shader stage
* @param tcs_code Source code for tesselation control shader stage
* @param tes_code Source code for tesselation evaluation shader stage
* @param vs_code Source code for vertex shader stage
*
* @return ID of program object
**/
GLuint Utils::createAndBuildProgram(deqp::Context& context, const GLchar* cs_code, const GLchar* fs_code,
const GLchar* gs_code, const GLchar* tcs_code, const GLchar* tes_code,
const GLchar* vs_code)
{
#define N_SHADER_STAGES 6
const Functions& gl = context.getRenderContext().getFunctions();
GLuint id = 0;
GLuint shader_ids[N_SHADER_STAGES] = { 0 };
const GLchar* shader_sources[N_SHADER_STAGES] = { cs_code, fs_code, gs_code, tcs_code, tes_code, vs_code };
const GLenum shader_types[N_SHADER_STAGES] = { GL_COMPUTE_SHADER, GL_FRAGMENT_SHADER,
GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER,
GL_TESS_EVALUATION_SHADER, GL_VERTEX_SHADER };
GLint status = GL_FALSE;
/* Compile all shaders */
try
{
for (GLuint i = 0; i < N_SHADER_STAGES; ++i)
{
if (0 != shader_sources[i])
{
shader_ids[i] = createAndCompileShader(context, shader_types[i], shader_sources[i]);
}
}
/* Check compilation */
for (GLuint i = 0; i < N_SHADER_STAGES; ++i)
{
if ((0 != shader_sources[i]) && (0 == shader_ids[i]))
{
context.getTestContext().getLog() << tcu::TestLog::Message
<< "Failed to build program due to compilation problems"
<< tcu::TestLog::EndMessage;
/* Delete shaders */
for (GLuint j = 0; j < N_SHADER_STAGES; ++j)
{
deleteShader(context, shader_ids[j]);
}
/* Done */
return 0;
}
}
/* Create program */
id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram");
/* Attach shaders */
for (GLuint i = 0; i < N_SHADER_STAGES; ++i)
{
if (0 != shader_ids[i])
{
gl.attachShader(id, shader_ids[i]);
GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
}
}
/* Link program */
gl.linkProgram(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram");
/* Clean shaders */
for (GLuint j = 0; j < N_SHADER_STAGES; ++j)
{
deleteShader(context, shader_ids[j]);
}
/* Get link status */
gl.getProgramiv(id, GL_LINK_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
/* Log link error */
if (GL_TRUE != status)
{
glw::GLint length = 0;
std::string message;
/* Get error log length */
gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
message.resize(length, 0);
/* Get error log */
gl.getProgramInfoLog(id, length, 0, &message[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog");
context.getTestContext().getLog() << tcu::TestLog::Message << "Program linking failed: " << message
<< tcu::TestLog::EndMessage;
/* Clean program */
deleteProgram(context, id);
/* Done */
return 0;
}
}
catch (std::exception& exc)
{
/* Delete shaders */
for (GLuint j = 0; j < N_SHADER_STAGES; ++j)
{
deleteShader(context, shader_ids[j]);
}
throw exc;
}
return id;
}
/** Create and compile shader
*
* @param context Test context
* @param type Type of shader
* @param code Source code for shader
*
* @return ID of shader object
**/
GLuint Utils::createAndCompileShader(deqp::Context& context, const GLenum type, const GLchar* code)
{
const Functions& gl = context.getRenderContext().getFunctions();
GLuint id = gl.createShader(type);
GLint status = GL_FALSE;
GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
try
{
gl.shaderSource(id, 1 /* count */, &code, 0 /* lengths */);
GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource");
/* Compile */
gl.compileShader(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader");
/* Get compilation status */
gl.getShaderiv(id, GL_COMPILE_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Log compilation error */
if (GL_TRUE != status)
{
glw::GLint length = 0;
std::string message;
/* Error log length */
gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &length);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
/* Prepare storage */
message.resize(length, 0);
/* Get error log */
gl.getShaderInfoLog(id, length, 0, &message[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog");
context.getTestContext().getLog() << tcu::TestLog::Message << "Shader (" << glu::getShaderTypeStr(type)
<< ") compilation failed: " << message << tcu::TestLog::EndMessage;
deleteShader(context, id);
id = 0;
}
}
catch (std::exception& exc)
{
deleteShader(context, id);
throw exc;
}
return id;
}
/** Generate and fill 2d texture
*
* @param context Test context
* @param width Width of texture
* @param height Height of texture
* @param internal_format Internal format of texture
* @param format Format of data
* @param type Type of data
* @param data Data
*
* @return ID of texture object
**/
GLuint Utils::createAndFill2DTexture(deqp::Context& context, GLuint width, GLuint height, GLenum internal_format,
GLenum format, GLenum type, const GLvoid* data)
{
const Functions& gl = context.getRenderContext().getFunctions();
GLuint id = 0;
gl.genTextures(1, &id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures");
try
{
gl.bindTexture(GL_TEXTURE_2D, id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.texImage2D(GL_TEXTURE_2D, 0 /* level */, internal_format, width, height, 0 /* border */, format, type, data);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D");
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri");
gl.bindTexture(GL_TEXTURE_2D, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
}
catch (std::exception& exc)
{
gl.deleteTextures(1, &id);
id = 0;
throw exc;
}
return id;
}
/** Delete program
*
* @param context Test context
* @param id ID of program
**/
void Utils::deleteProgram(deqp::Context& context, const GLuint id)
{
const glw::Functions& gl = context.getRenderContext().getFunctions();
if (0 != id)
{
gl.deleteProgram(id);
}
}
/** Delete shader
*
* @param context Test context
* @param id ID of shader
**/
void Utils::deleteShader(deqp::Context& context, const GLuint id)
{
const glw::Functions& gl = context.getRenderContext().getFunctions();
if (0 != id)
{
gl.deleteShader(id);
}
}
/** Delete texture
*
* @param context Test context
* @param id ID of texture
**/
void Utils::deleteTexture(deqp::Context& context, const GLuint id)
{
const glw::Functions& gl = context.getRenderContext().getFunctions();
if (0 != id)
{
gl.deleteTextures(1, &id);
}
}
/** Checks if extensions is not available.
*
* @param context Test context
* @param extension_name Name of extension
*
* @return true if extension is reported as available, false otherwise
**/
bool Utils::isExtensionSupported(deqp::Context& context, const GLchar* extension_name)
{
const std::vector<std::string>& extensions = context.getContextInfo().getExtensions();
if (std::find(extensions.begin(), extensions.end(), extension_name) == extensions.end())
{
std::string message = "Required extension is not supported: ";
message.append(extension_name);
return false;
}
return true;
}
/** Replace first occurance of <token> with <text> in <string> starting at <search_posistion>
*
* @param token Token string
* @param search_position Position at which find will start, it is updated to position at which replaced text ends
* @param text String that will be used as replacement for <token>
* @param string String to work on
**/
void Utils::replaceToken(const GLchar* token, size_t& search_position, const GLchar* text, std::string& string)
{
const size_t text_length = strlen(text);
const size_t token_length = strlen(token);
const size_t token_position = string.find(token, search_position);
#if DEBUG_REPLACE_TOKEN
if (std::string::npos == token_position)
{
string.append("\n\nInvalid token: ");
string.append(token);
TCU_FAIL(string.c_str());
}
#endif /* DEBUG_REPLACE_TOKEN */
string.replace(token_position, token_length, text, text_length);
search_position = token_position + text_length;
}
/* FunctionalTest */
/* Shader sources */
const GLchar* FunctionalTest::m_compute_shader_code =
"#version 430 core\n"
"\n"
"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
"\n"
"IMAGE_DEFINITION;\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" vec2 tex_coord = vec2(gl_GlobalInvocationID.xy) / 8.0;\n"
"\n"
" imageStore(uni_image,\n"
" ivec2(gl_GlobalInvocationID.xy),\n"
" TYPE(texture(uni_sampler, tex_coord).r, 0, 0, 0));\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_fragment_shader_code =
"#version 430 core\n"
"\n"
" in vec2 gs_fs_tex_coord;\n"
"flat in uint gs_fs_result;\n"
" out TYPE fs_out_result;\n"
"\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" if (1 != gs_fs_result)\n"
" {\n"
" fs_out_result = texture(uni_sampler, vec2(0.9375, 0.9375));\n"
" }\n"
" else\n"
" {\n"
" fs_out_result = texture(uni_sampler, gs_fs_tex_coord);\n"
" }\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_geometry_shader_code =
"#version 430 core\n"
"\n"
"layout(points) in;\n"
"layout(triangle_strip, max_vertices = 4) out;\n"
"\n"
" in uint tes_gs_result[];\n"
"flat out uint gs_fs_result;\n"
" out vec2 gs_fs_tex_coord;\n"
"\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" uint result = 1u;\n"
"\n"
" if (1 != tes_gs_result[0])\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" if (EXPECTED_VALUE != texture(uni_sampler, vec2(0.9375, 0.9375)).r)\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" gs_fs_result = result;\n"
" gs_fs_tex_coord = vec2(0, 0);\n"
" gl_Position = vec4(-1, -1, 0, 1);\n"
" EmitVertex();\n"
" gs_fs_result = result;\n"
" gs_fs_tex_coord = vec2(0, 1);\n"
" gl_Position = vec4(-1, 1, 0, 1);\n"
" EmitVertex();\n"
" gs_fs_result = result;\n"
" gs_fs_tex_coord = vec2(1, 0);\n"
" gl_Position = vec4(1, -1, 0, 1);\n"
" EmitVertex();\n"
" gs_fs_result = result;\n"
" gs_fs_tex_coord = vec2(1, 1);\n"
" gl_Position = vec4(1, 1, 0, 1);\n"
" EmitVertex();\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_tesselation_control_shader_code =
"#version 430 core\n"
"\n"
"layout(vertices = 1) out;\n"
"\n"
"in uint vs_tcs_result[];\n"
"out uint tcs_tes_result[];\n"
"\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" uint result = 1u;\n"
"\n"
" if (1u != vs_tcs_result[gl_InvocationID])\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" if (EXPECTED_VALUE != texture(uni_sampler, vec2(0.9375, 0.9375)).r)\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" tcs_tes_result[gl_InvocationID] = result;\n"
"\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_tesselation_evaluation_shader_code =
"#version 430 core\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
"in uint tcs_tes_result[];\n"
"out uint tes_gs_result;\n"
"\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" uint result = 1u;\n"
"\n"
" if (1u != tcs_tes_result[0])\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" if (EXPECTED_VALUE != texture(uni_sampler, vec2(0.9375, 0.9375)).r)\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" tes_gs_result = result;\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_vertex_shader_code =
"#version 430 core\n"
"\n"
"out uint vs_tcs_result;\n"
"\n"
"SAMPLER_DEFINITION;\n"
"\n"
"void main()\n"
"{\n"
" uint result = 1u;\n"
"\n"
" if (EXPECTED_VALUE != texture(uni_sampler, vec2(0.9375, 0.9375)).r)\n"
" {\n"
" result = 0u;\n"
" }\n"
"\n"
" vs_tcs_result = result;\n"
"}\n"
"\n";
const GLchar* FunctionalTest::m_expected_value_depth = "0.0";
const GLchar* FunctionalTest::m_expected_value_stencil = "15u";
const GLchar* FunctionalTest::m_image_definition_depth = "writeonly uniform image2D uni_image";
const GLchar* FunctionalTest::m_image_definition_stencil = "writeonly uniform uimage2D uni_image";
const GLchar* FunctionalTest::m_output_type_depth = "vec4";
const GLchar* FunctionalTest::m_output_type_stencil = "uvec4";
const GLchar* FunctionalTest::m_sampler_definition_depth = "uniform sampler2D uni_sampler";
const GLchar* FunctionalTest::m_sampler_definition_stencil = "uniform usampler2D uni_sampler";
/* Constants */
const GLuint FunctionalTest::m_height = 8;
const GLint FunctionalTest::m_image_unit = 1;
const GLint FunctionalTest::m_texture_unit = 1;
const GLuint FunctionalTest::m_width = 8;
/** Constructor
*
* @param context Test context
**/
FunctionalTest::FunctionalTest(deqp::Context& context)
: TestCase(context, "functional", "Checks if sampling stencil texture gives expected results")
{
/* Nothing to be done here */
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult FunctionalTest::iterate()
{
bool test_result = true;
if (false == test(GL_DEPTH24_STENCIL8, true))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Test failed. Case format: GL_DEPTH24_STENCIL8, channel: S"
<< tcu::TestLog::EndMessage;
test_result = false;
}
if (false == test(GL_DEPTH24_STENCIL8, false))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Test failed. Case format: GL_DEPTH24_STENCIL8, channel: D"
<< tcu::TestLog::EndMessage;
test_result = false;
}
if (false == test(GL_DEPTH32F_STENCIL8, true))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Test failed. Case format: GL_DEPTH32F_STENCIL8, channel: S"
<< tcu::TestLog::EndMessage;
test_result = false;
}
if (false == test(GL_DEPTH32F_STENCIL8, false))
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Test failed. Case format: GL_DEPTH32F_STENCIL8, channel: D"
<< tcu::TestLog::EndMessage;
test_result = false;
}
/* Set result */
if (true == test_result)
{
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Execute compute program
*
* @param program_id ID of program
* @param is_stencil Selects if stencil or depth channel is sampled
* @param dst_texture_id ID of destination texture
* @param src_texture_id ID of source texture
**/
void FunctionalTest::dispatch(GLuint program_id, bool is_stencil, GLuint dst_texture_id, GLuint src_texture_id)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
GLenum internal_format = GL_R8UI;
GLint uni_image_loc = -1;
GLint uni_sampler_loc = -1;
if (false == is_stencil)
{
internal_format = GL_R32F;
}
/* Set program */
gl.useProgram(program_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
/* Get uniform location and bind texture to proper image unit */
uni_image_loc = gl.getUniformLocation(program_id, "uni_image");
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
gl.bindImageTexture(m_image_unit, dst_texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* Layer */,
GL_WRITE_ONLY, internal_format);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
gl.uniform1i(uni_image_loc, m_image_unit);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
/* Get uniform location and bind texture to proper texture unit */
uni_sampler_loc = gl.getUniformLocation(program_id, "uni_sampler");
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
gl.activeTexture(GL_TEXTURE0 + m_texture_unit);
GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
gl.bindTexture(GL_TEXTURE_2D, src_texture_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.uniform1i(uni_sampler_loc, m_texture_unit);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
/* Dispatch program */
gl.dispatchCompute(m_width, m_height, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
/* Sync */
gl.memoryBarrier(GL_ALL_BARRIER_BITS);
GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
}
/** Execute draw program
*
* @param program_id ID of program
* @param dst_texture_id ID of destination texture
* @param src_texture_id ID of source texture
**/
void FunctionalTest::draw(GLuint program_id, GLuint dst_texture_id, GLuint src_texture_id)
{
GLuint fb_id = 0;
const Functions& gl = m_context.getRenderContext().getFunctions();
GLint uni_sampler_loc = -1;
GLuint vao_id = 0;
try
{
/* Tesselation patch set up */
gl.patchParameteri(GL_PATCH_VERTICES, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "PatchParameteri");
/* Prepare VAO */
gl.genVertexArrays(1, &vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays");
gl.bindVertexArray(vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray");
/* Prepare FBO */
gl.genFramebuffers(1, &fb_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
gl.framebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dst_texture_id, 0 /* level */);
GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture");
gl.viewport(0 /* x */, 0 /* y */, m_width, m_height);
GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
/* Set program */
gl.useProgram(program_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
/* Get uniform location and bind texture to proper texture unit */
uni_sampler_loc = gl.getUniformLocation(program_id, "uni_sampler");
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformLocation");
gl.activeTexture(GL_TEXTURE0 + m_texture_unit);
GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
gl.bindTexture(GL_TEXTURE_2D, src_texture_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.uniform1i(uni_sampler_loc, m_texture_unit);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
/* Draw */
gl.drawArrays(GL_PATCHES, 0 /* first */, 1 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
/* Sync */
gl.memoryBarrier(GL_ALL_BARRIER_BITS);
GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
}
catch (std::exception& exc)
{
gl.bindVertexArray(0);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
if (0 != vao_id)
{
gl.deleteVertexArrays(1, &vao_id);
}
if (0 != fb_id)
{
gl.deleteFramebuffers(1, &fb_id);
}
throw exc;
}
gl.bindVertexArray(0);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
if (0 != vao_id)
{
gl.deleteVertexArrays(1, &vao_id);
}
if (0 != fb_id)
{
gl.deleteFramebuffers(1, &fb_id);
}
}
/** Prepare destination texture
*
* @param is_stencil Selects if stencil or depth channel is sampled
*
* @return ID of texture
**/
GLuint FunctionalTest::prepareDestinationTexture(bool is_stencil)
{
static const GLuint n_pixels = m_width * m_height;
GLenum format = 0;
GLenum internal_format = 0;
GLuint pixel_size = 0;
std::vector<GLubyte> texture_data;
GLuint texture_id = 0;
GLuint texture_size = 0;
GLenum type = 0;
/* Select size of pixel */
if (true == is_stencil)
{
format = GL_RED_INTEGER;
internal_format = GL_R8UI;
pixel_size = 1;
type = GL_UNSIGNED_BYTE;
}
else
{
format = GL_RED;
internal_format = GL_R32F;
pixel_size = 4;
type = GL_FLOAT;
}
/* Allocate storage */
texture_size = pixel_size * n_pixels;
texture_data.resize(texture_size);
/* Fill texture data */
memset(&texture_data[0], 0, texture_size);
/* Create texture */
texture_id =
Utils::createAndFill2DTexture(m_context, m_width, m_height, internal_format, format, type, &texture_data[0]);
/* Done */
return texture_id;
}
/** Prepare program
*
* @param is_draw Selects if draw or compute program is prepared
* @param is_stencil Selects if stencil or depth channel is sampled
*
* @return ID of texture
**/
GLuint FunctionalTest::prepareProgram(bool is_draw, bool is_stencil)
{
GLuint program_id = 0;
if (true != is_draw)
{
std::string cs_code = m_compute_shader_code;
const GLchar* image_definition = m_image_definition_stencil;
size_t position = 0;
const GLchar* sampler_definition = m_sampler_definition_stencil;
const GLchar* type = m_output_type_stencil;
if (false == is_stencil)
{
image_definition = m_image_definition_depth;
sampler_definition = m_sampler_definition_depth;
type = m_output_type_depth;
}
Utils::replaceToken("IMAGE_DEFINITION", position, image_definition, cs_code);
Utils::replaceToken("SAMPLER_DEFINITION", position, sampler_definition, cs_code);
Utils::replaceToken("TYPE", position, type, cs_code);
program_id = Utils::createAndBuildProgram(m_context, cs_code.c_str(), 0 /* fs_code */, 0 /* gs_code */,
0 /* tcs_code */, 0 /* tes_code */, 0 /* vs_code */);
}
else
{
#define N_FUNCTIONAL_TEST_SHADER_STAGES 5
const GLchar* expected_value = m_expected_value_stencil;
const GLchar* sampler_definition = m_sampler_definition_stencil;
std::string shader_code[N_FUNCTIONAL_TEST_SHADER_STAGES];
const GLchar* shader_templates[N_FUNCTIONAL_TEST_SHADER_STAGES] = {
m_fragment_shader_code, m_geometry_shader_code, m_tesselation_control_shader_code,
m_tesselation_evaluation_shader_code, m_vertex_shader_code
};
const GLchar* type = m_output_type_stencil;
if (false == is_stencil)
{
expected_value = m_expected_value_depth;
sampler_definition = m_sampler_definition_depth;
type = m_output_type_depth;
}
for (GLuint i = 0; i < N_FUNCTIONAL_TEST_SHADER_STAGES; ++i)
{
size_t position = 0;
shader_code[i] = shader_templates[i];
if (0 == i)
{
Utils::replaceToken("TYPE", position, type, shader_code[i]);
Utils::replaceToken("SAMPLER_DEFINITION", position, sampler_definition, shader_code[i]);
//Utils::replaceToken("TYPE", position, type, shader_code[i]);
}
else
{
Utils::replaceToken("SAMPLER_DEFINITION", position, sampler_definition, shader_code[i]);
Utils::replaceToken("EXPECTED_VALUE", position, expected_value, shader_code[i]);
}
}
program_id =
Utils::createAndBuildProgram(m_context, 0 /* cs_code */, shader_code[0].c_str() /* fs_code */,
shader_code[1].c_str() /* gs_code */, shader_code[2].c_str() /* tcs_code */,
shader_code[3].c_str() /* tes_code */, shader_code[4].c_str() /* vs_code */);
}
/* Done */
return program_id;
}
/** Prepare source texture
*
* @param internal_format Internal format of texture
* @param is_stencil Selects if stencil or depth channel is sampled
* @param texture_data Texture contents
*
* @return ID of texture
**/
GLuint FunctionalTest::prepareSourceTexture(GLenum internal_format, bool is_stencil,
const std::vector<glw::GLubyte>& texture_data)
{
const Functions& gl = m_context.getRenderContext().getFunctions();
GLuint texture_id = 0;
GLenum type = 0;
/* Select size of pixel */
switch (internal_format)
{
case GL_DEPTH24_STENCIL8:
type = GL_UNSIGNED_INT_24_8;
break;
case GL_DEPTH32F_STENCIL8:
type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
break;
default:
TCU_FAIL("Invalid enum");
break;
}
/* Create texture */
texture_id = Utils::createAndFill2DTexture(m_context, m_width, m_height, internal_format, GL_DEPTH_STENCIL, type,
&texture_data[0]);
/* Set DS texture mode */
gl.bindTexture(GL_TEXTURE_2D, texture_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
if (true == is_stencil)
{
gl.texParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri");
}
else
{
gl.texParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri");
}
/* Set nearest filtering */
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri");
/* Unbind */
gl.bindTexture(GL_TEXTURE_2D, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
/* Done */
return texture_id;
}
/** Prepare data for source texture
*
* @param internal_format Internal format of texture
* @param texture_data Texture contents
*
* @return ID of texture
**/
void FunctionalTest::prepareSourceTextureData(GLenum internal_format, std::vector<GLubyte>& texture_data)
{
static const GLfloat depth_step_h = -0.5f / ((GLfloat)(m_width - 1));
static const GLfloat depth_step_v = -0.5f / ((GLfloat)(m_height - 1));
static const GLuint stencil_step_h = 1;
static const GLuint stencil_step_v = 1;
static const GLuint n_pixels = m_width * m_height;
GLuint pixel_size = 0;
GLuint line_size = 0;
GLuint texture_size = 0;
/* Select size of pixel */
switch (internal_format)
{
case GL_DEPTH24_STENCIL8:
pixel_size = 4;
break;
case GL_DEPTH32F_STENCIL8:
pixel_size = 8;
break;
default:
TCU_FAIL("Invalid enum");
break;
}
line_size = pixel_size * m_width;
texture_size = pixel_size * n_pixels;
/* Allocate storage */
texture_data.resize(texture_size);
/* Fill texture data */
for (GLuint y = 0; y < m_height; ++y)
{
const GLfloat depth_v = depth_step_v * (GLfloat)y;
const GLuint line_offset = line_size * y;
const GLuint stencil_v = stencil_step_v * y;
for (GLuint x = 0; x < m_width; ++x)
{
const GLfloat depth_h = depth_step_h * (GLfloat)x;
const GLfloat depth_f = 1 + depth_h + depth_v;
const GLuint depth_i = (GLuint)(((GLfloat)0xffffff) * depth_f);
const GLuint pixel_offset = pixel_size * x;
const GLuint stencil_h = stencil_step_h * x;
const GLuint stencil = 1 + stencil_h + stencil_v;
GLubyte* depth_f_data = (GLubyte*)&depth_f;
GLubyte* depth_i_data = (GLubyte*)&depth_i;
GLubyte* pixel_data = &texture_data[0] + line_offset + pixel_offset;
GLubyte* stencil_data = (GLubyte*)&stencil;
switch (pixel_size)
{
case 4:
memcpy(pixel_data, stencil_data, 1);
memcpy(pixel_data + 1, depth_i_data, 3);
break;
case 8:
memcpy(pixel_data, depth_f_data, 4);
memcpy(pixel_data + 4, stencil_data, 1);
break;
default:
TCU_FAIL("Invalid value");
break;
}
}
}
}
/** Verifies that destination texture contents match expectations
*
* @param id ID of destination texture
* @param source_internal_format Internal format of source texture
* @param is_stencil Selects if stencil of depth channel is sampled
* @param src_texture_data Contents of source texture
*
* @return true if everything is fine, false otherwise
**/
bool FunctionalTest::verifyTexture(GLuint id, GLenum source_internal_format, bool is_stencil,
const std::vector<GLubyte>& src_texture_data)
{
static const GLuint n_pixels = m_width * m_height;
const Functions& gl = m_context.getRenderContext().getFunctions();
GLuint dst_pixel_size = 0;
std::vector<GLubyte> dst_texture_data;
GLuint dst_texture_size = 0;
GLenum format = 0;
GLuint src_pixel_size = 0;
GLuint src_stencil_off = 0;
GLenum type = 0;
/* Select size of pixel */
if (true == is_stencil)
{
format = GL_RED_INTEGER;
dst_pixel_size = 1;
type = GL_UNSIGNED_BYTE;
}
else
{
format = GL_RED;
dst_pixel_size = 4;
type = GL_FLOAT;
}
if (GL_DEPTH24_STENCIL8 == source_internal_format)
{
src_pixel_size = 4;
}
else
{
src_pixel_size = 8;
src_stencil_off = 4;
}
/* Allocate storage */
dst_texture_size = dst_pixel_size * n_pixels;
dst_texture_data.resize(dst_texture_size);
/* Get texture contents */
gl.bindTexture(GL_TEXTURE_2D, id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
gl.getTexImage(GL_TEXTURE_2D, 0 /* level */, format, type, &dst_texture_data[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage");
gl.bindTexture(GL_TEXTURE_2D, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
/* For each pixel */
for (GLuint i = 0; i < n_pixels; ++i)
{
const GLuint dst_pixel_offset = dst_pixel_size * i;
const GLuint src_pixel_offset = src_pixel_size * i;
const GLubyte* dst_pixel_data = &dst_texture_data[0] + dst_pixel_offset;
const GLubyte* src_pixel_data = &src_texture_data[0] + src_pixel_offset;
if (true == is_stencil) /* Stencil channel */
{
const GLubyte dst_stencil = dst_pixel_data[0];
const GLubyte src_stencil = src_pixel_data[src_stencil_off];
if (src_stencil != dst_stencil)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixel [" << i
<< "], got: " << (GLuint)dst_stencil
<< " expected: " << (GLuint)src_stencil << tcu::TestLog::EndMessage;
return false;
}
}
else /* Depth channel */
{
if (GL_DEPTH24_STENCIL8 == source_internal_format) /* DEPTH24 */
{
GLfloat dst_depth = 0.0f;
GLuint src_depth_i = 0;
GLfloat src_depth_f = 0.0f;
memcpy(&dst_depth, dst_pixel_data, 4);
memcpy(&src_depth_i, src_pixel_data + 1, 3);
src_depth_f = ((GLfloat)src_depth_i) / ((GLfloat)0xffffff);
if (de::abs(src_depth_f - dst_depth) > 0.0001f)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixel [" << i
<< "], got: " << dst_depth << " expected: " << src_depth_f
<< tcu::TestLog::EndMessage;
return false;
}
}
else /* DEPTH32F */
{
GLfloat dst_depth = 0.0f;
GLfloat src_depth = 0.0f;
memcpy(&dst_depth, dst_pixel_data, 4);
memcpy(&src_depth, src_pixel_data, 4);
if (de::abs(src_depth - dst_depth) > 0.0001f)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixel [" << i
<< "], got: " << dst_depth << " expected: " << src_depth
<< tcu::TestLog::EndMessage;
return false;
}
}
}
}
return true;
}
/** Test given internal format and channel
*
* @param internal_format Internal fromat of source texture
* @param is_stencil Selects if stencil or depth channel is sampled
*
* @return true if results from compute and draw programs are positive, false otherwise
**/
bool FunctionalTest::test(GLenum internal_format, bool is_stencil)
{
GLuint compute_dst_tex_id = 0;
GLuint compute_program_id = 0;
GLuint compute_src_tex_id = 0;
GLuint draw_dst_tex_id = 0;
GLuint draw_program_id = 0;
GLuint draw_src_tex_id = 0;
const Functions& gl = m_context.getRenderContext().getFunctions();
bool test_result = true;
std::vector<GLubyte> texture_data;
prepareSourceTextureData(internal_format, texture_data);
try
{
if (true == Utils::isExtensionSupported(m_context, "GL_ARB_compute_shader"))
{
compute_dst_tex_id = prepareDestinationTexture(is_stencil);
compute_program_id = prepareProgram(false, is_stencil);
compute_src_tex_id = prepareSourceTexture(internal_format, is_stencil, texture_data);
dispatch(compute_program_id, is_stencil, compute_dst_tex_id, compute_src_tex_id);
if (false == verifyTexture(compute_dst_tex_id, internal_format, is_stencil, texture_data))
{
test_result = false;
}
}
{
draw_dst_tex_id = prepareDestinationTexture(is_stencil);
draw_program_id = prepareProgram(true, is_stencil);
draw_src_tex_id = prepareSourceTexture(internal_format, is_stencil, texture_data);
draw(draw_program_id, draw_dst_tex_id, draw_src_tex_id);
if (false == verifyTexture(draw_dst_tex_id, internal_format, is_stencil, texture_data))
{
test_result = false;
}
}
}
catch (std::exception& exc)
{
gl.bindVertexArray(0);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.useProgram(0);
Utils::deleteProgram(m_context, compute_program_id);
Utils::deleteProgram(m_context, draw_program_id);
Utils::deleteTexture(m_context, compute_dst_tex_id);
Utils::deleteTexture(m_context, compute_src_tex_id);
Utils::deleteTexture(m_context, draw_dst_tex_id);
Utils::deleteTexture(m_context, draw_src_tex_id);
TCU_FAIL(exc.what());
}
gl.bindVertexArray(0);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.useProgram(0);
Utils::deleteProgram(m_context, compute_program_id);
Utils::deleteProgram(m_context, draw_program_id);
Utils::deleteTexture(m_context, compute_dst_tex_id);
Utils::deleteTexture(m_context, compute_src_tex_id);
Utils::deleteTexture(m_context, draw_dst_tex_id);
Utils::deleteTexture(m_context, draw_src_tex_id);
/* Done */
return test_result;
}
} /* namespace StencilTexturing */
StencilTexturingTests::StencilTexturingTests(deqp::Context& context) : TestCaseGroup(context, "stencil_texturing", "")
{
}
StencilTexturingTests::~StencilTexturingTests(void)
{
}
void StencilTexturingTests::init()
{
addChild(new StencilTexturing::FunctionalTest(m_context));
}
} /* namespace gl4cts */