/*------------------------------------------------------------------------- * 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 #include #include #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& 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 with in starting at * * @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 * @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 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& 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"); } /* 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& 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"); } 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"); } } } } /** 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& 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 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 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 */