/*------------------------------------------------------------------------- * 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 "deMath.h" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "gl4cES31CompatibilityTests.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 tcu { static bool operator<(tcu::Vec4 const& k1, tcu::Vec4 const& k2) { if (k1.y() < k2.y()) { return true; } else if (k1.y() == k2.y()) { return k1.x() < k2.x(); } else { return false; } } } namespace gl4cts { namespace es31compatibility { using tcu::TestLog; using std::string; using std::vector; using deqp::Context; static std::string specializeVersion(std::string const& source, glu::GLSLVersion version, std::string const& sampler = "", std::string const& outType = "") { DE_ASSERT(version == glu::GLSL_VERSION_310_ES || version >= glu::GLSL_VERSION_400); std::map args; args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(version); args["SAMPLER"] = sampler; args["OUT_TYPE"] = outType; if (version == glu::GLSL_VERSION_310_ES) { args["OES_SV_RQ"] = "#extension GL_OES_sample_variables : require\n"; args["OES_SV_EN"] = "#extension GL_OES_sample_variables : enable\n"; } else { args["OES_SV_RQ"] = ""; args["OES_SV_EN"] = ""; } return tcu::StringTemplate(source.c_str()).specialize(args); } class SampleShadingExtensionCase : public deqp::TestCase { public: SampleShadingExtensionCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion); ~SampleShadingExtensionCase(); IterateResult iterate(); protected: glu::GLSLVersion m_glslVersion; }; SampleShadingExtensionCase::SampleShadingExtensionCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion) : TestCase(context, name, description), m_glslVersion(glslVersion) { DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES); } SampleShadingExtensionCase::~SampleShadingExtensionCase() { } SampleShadingExtensionCase::IterateResult SampleShadingExtensionCase::iterate() { TestLog& log = m_testCtx.getLog(); /* OpenGL support query. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_es31_compatibility = m_context.getContextInfo().isExtensionSupported("GL_ARB_ES3_1_compatibility"); if (!(is_at_least_gl_45 || is_arb_es31_compatibility)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_ARB_ES3_1_compatibility"); return STOP; } static char const* vss = "${VERSION_DECL}\n" "in highp vec4 a_position;\n" "void main()\n" "{\n" " gl_Position = a_position;\n" "}\n"; { static char const* fss = "${VERSION_DECL}\n" "${OES_SV_RQ}" "out highp vec4 o_color;\n" "void main()\n" "{\n" " for (int i = 0; i < (gl_MaxSamples + 31) / 32; ++i) {\n" " gl_SampleMask[i] = gl_SampleMaskIn[i];\n" " }\n" " o_color = vec4(gl_SampleID, gl_SamplePosition.x, gl_SamplePosition.y, 1);\n" "}\n"; glu::ShaderProgram programRequire(m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion).c_str(), specializeVersion(fss, m_glslVersion).c_str())); log << programRequire; if (!programRequire.isOk()) { TCU_FAIL("Compile failed"); } } { static char const* fss = "${VERSION_DECL}\n" "${OES_SV_EN}" "out highp vec4 o_color;\n" "void main()\n" "{\n" "#if !GL_OES_sample_variables\n" " this is broken\n" "#endif\n" " for (int i = 0; i < (gl_MaxSamples + 31) / 32; ++i) {\n" " gl_SampleMask[i] = gl_SampleMaskIn[i];\n" " }\n" " o_color = vec4(gl_SampleID, gl_SamplePosition.x, gl_SamplePosition.y, 1);\n" "}\n"; glu::ShaderProgram programEnable(m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion).c_str(), specializeVersion(fss, m_glslVersion).c_str())); log << programEnable; if (!programEnable.isOk()) { TCU_FAIL("Compile failed"); } } m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } class SampleShadingMaskCase : public deqp::TestCase { public: SampleShadingMaskCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLenum internalFormat, tcu::TextureFormat const& texFormat, const char* sampler, const char* outType, GLint samples, GLint sampleMask); ~SampleShadingMaskCase(); IterateResult iterate(); protected: glu::GLSLVersion m_glslVersion; GLenum m_internalFormat; tcu::TextureFormat m_texFormat; std::string m_sampler; std::string m_outType; GLint m_samples; GLint m_sampleMask; enum { WIDTH = 16, HEIGHT = 16, MAX_SAMPLES = 4, }; }; SampleShadingMaskCase::SampleShadingMaskCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLenum internalFormat, tcu::TextureFormat const& texFormat, const char* sampler, const char* outType, GLint samples, GLint sampleMask) : TestCase(context, name, description) , m_glslVersion(glslVersion) , m_internalFormat(internalFormat) , m_texFormat(texFormat) , m_sampler(sampler) , m_outType(outType) , m_samples(samples) , m_sampleMask(sampleMask) { DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion >= glu::GLSL_VERSION_400); } SampleShadingMaskCase::~SampleShadingMaskCase() { } SampleShadingMaskCase::IterateResult SampleShadingMaskCase::iterate() { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool isOk = true; /* OpenGL support query. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_es31_compatibility = m_context.getContextInfo().isExtensionSupported("GL_ARB_ES3_1_compatibility"); if (!(is_at_least_gl_45 || is_arb_es31_compatibility)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_ARB_ES3_1_compatibility"); return STOP; } GLint maxSamples; if (((m_texFormat.type == tcu::TextureFormat::FLOAT) && (m_texFormat.order == tcu::TextureFormat::RGBA)) || ((m_texFormat.type == tcu::TextureFormat::FLOAT) && (m_texFormat.order == tcu::TextureFormat::RG)) || ((m_texFormat.type == tcu::TextureFormat::FLOAT) && (m_texFormat.order == tcu::TextureFormat::R)) || ((m_texFormat.type == tcu::TextureFormat::HALF_FLOAT) && (m_texFormat.order == tcu::TextureFormat::RGBA))) { gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, m_internalFormat, GL_SAMPLES, 1, &maxSamples); if (m_samples > maxSamples) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Test sample count greater than samples that the format supports"); return STOP; } } else if (m_texFormat.type == tcu::TextureFormat::SIGNED_INT8 || m_texFormat.type == tcu::TextureFormat::UNSIGNED_INT8) { gl.getIntegerv(GL_MAX_INTEGER_SAMPLES, &maxSamples); if (m_samples > maxSamples) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Test sample count greater than MAX_INTEGER_SAMPLES"); return STOP; } } else { gl.getIntegerv(GL_MAX_SAMPLES, &maxSamples); if (m_samples > maxSamples) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Test sample count greater than MAX_SAMPLES"); return STOP; } } // Create a multisample texture, or a regular texture if samples is zero. GLuint tex; gl.genTextures(1, &tex); GLenum target; if (m_samples) { target = GL_TEXTURE_2D_MULTISAMPLE; gl.bindTexture(target, tex); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_samples, m_internalFormat, WIDTH, HEIGHT, GL_FALSE); } else { target = GL_TEXTURE_2D; gl.bindTexture(target, tex); gl.texStorage2D(GL_TEXTURE_2D, 1, m_internalFormat, WIDTH, HEIGHT); if (m_texFormat.type == tcu::TextureFormat::SIGNED_INT8 || m_texFormat.type == tcu::TextureFormat::UNSIGNED_INT8 || m_texFormat.type == tcu::TextureFormat::FLOAT) { gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } } // Create a framebuffer with the texture attached and clear to "green". GLuint fboMs; gl.genFramebuffers(1, &fboMs); gl.bindFramebuffer(GL_FRAMEBUFFER, fboMs); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tex, 0); gl.viewport(0, 0, WIDTH, HEIGHT); if (m_texFormat.type == tcu::TextureFormat::SIGNED_INT8) { GLint color[4] = { 0, 1, 0, 1 }; gl.clearBufferiv(GL_COLOR, 0, color); } else if (m_texFormat.type == tcu::TextureFormat::UNSIGNED_INT8) { GLuint color[4] = { 0, 1, 0, 1 }; gl.clearBufferuiv(GL_COLOR, 0, color); } else { GLfloat color[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; gl.clearBufferfv(GL_COLOR, 0, color); } static deUint16 const quadIndices[] = { 0, 1, 2, 2, 1, 3 }; { // Draw a quad setting all samples to "red". We only expect "red" // to be written if the sample mask bit for that sample is 1. static char const* vss = "${VERSION_DECL}\n" "in highp vec2 a_position;\n" "void main()\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; static char const* fss = "${VERSION_DECL}\n" "${OES_SV_RQ}" "layout(location = 0) out highp ${OUT_TYPE} o_color;\n" "uniform int u_sampleMask;\n" "void main()\n" "{\n" " for (int i = 0; i < (gl_NumSamples + 31) / 32; ++i) {\n" " gl_SampleMask[i] = u_sampleMask & gl_SampleMaskIn[i];\n" " }\n" " o_color = ${OUT_TYPE}(1, 0, 0, 1);\n" "}\n"; glu::ShaderProgram program( m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion, m_sampler, m_outType).c_str(), specializeVersion(fss, m_glslVersion, m_sampler, m_outType).c_str())); log << program; if (!program.isOk()) { TCU_FAIL("Compile failed"); } static float const position[] = { -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, }; gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_sampleMask"), m_sampleMask); glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 2, 4, 0, &position[0]), }; glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw quad"); } gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); gl.deleteFramebuffers(1, &fboMs); GLsizei width = WIDTH * ((m_samples) ? m_samples : 1); GLuint rbo; gl.genRenderbuffers(1, &rbo); gl.bindRenderbuffer(GL_RENDERBUFFER, rbo); gl.renderbufferStorage(GL_RENDERBUFFER, m_internalFormat, width, HEIGHT); GLuint fbo; gl.genFramebuffers(1, &fbo); gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); gl.viewport(0, 0, width, HEIGHT); { // Resolve the multi-sample texture into a render-buffer sized such that // the width can hold all samples of a pixel. static char const* vss = "${VERSION_DECL}\n" "in highp vec2 a_position;\n" "void main(void)\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; static char const* fss = "${VERSION_DECL}\n" "uniform highp ${SAMPLER} u_tex;\n" "uniform highp ${SAMPLER}MS u_texMS;\n" "uniform int u_samples;\n" "layout(location = 0) out highp ${OUT_TYPE} o_color;\n" "void main(void)\n" "{\n" " if (u_samples > 0) {\n" " ivec2 coord = ivec2(int(gl_FragCoord.x) / u_samples, gl_FragCoord.y);\n" " int sampleId = int(gl_FragCoord.x) % u_samples;\n" " o_color = texelFetch(u_texMS, coord, sampleId);\n" " } else {\n" " ivec2 coord = ivec2(gl_FragCoord.x, gl_FragCoord.y);\n" " o_color = texelFetch(u_tex, coord, 0);\n" " }\n" "}\n"; glu::ShaderProgram program( m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion, m_sampler, m_outType).c_str(), specializeVersion(fss, m_glslVersion, m_sampler, m_outType).c_str())); log << program; if (!program.isOk()) { TCU_FAIL("Compile failed"); } static float const position[] = { -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, }; gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_samples"), m_samples); if (m_samples > 0) { // only MS sampler needed, TU 1 is not used gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_tex"), 1); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_texMS"), 0); } else { // only non-MS sampler needed, TU 1 is not used gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_tex"), 0); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_texMS"), 1); } glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 2, 4, 0, &position[0]), }; glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw quad"); } tcu::TextureLevel textureLevel(m_texFormat, width, HEIGHT); tcu::PixelBufferAccess pixels = textureLevel.getAccess(); std::vector result(pixels.getHeight() * pixels.getWidth()); if (pixels.getFormat().type == tcu::TextureFormat::SIGNED_INT8) { std::vector data(pixels.getHeight() * pixels.getWidth() * 4); gl.readPixels(0, 0, pixels.getWidth(), pixels.getHeight(), GL_RGBA_INTEGER, GL_INT, &data[0]); for (unsigned int i = 0; i < data.size(); i += 4) { result[i / 4] = tcu::Vec4((GLfloat)data[i], (GLfloat)data[i + 1], (GLfloat)data[i + 2], (GLfloat)data[i + 3]); } } else if (pixels.getFormat().type == tcu::TextureFormat::UNSIGNED_INT8) { std::vector data(pixels.getHeight() * pixels.getWidth() * 4); gl.readPixels(0, 0, pixels.getWidth(), pixels.getHeight(), GL_RGBA_INTEGER, GL_UNSIGNED_INT, &data[0]); for (unsigned int i = 0; i < data.size(); i += 4) { result[i / 4] = tcu::Vec4((GLfloat)data[i], (GLfloat)data[i + 1], (GLfloat)data[i + 2], (GLfloat)data[i + 3]); } } else { glu::readPixels(m_context.getRenderContext(), 0, 0, pixels); } for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { GLint samples = (m_samples) ? m_samples : 1; for (int sample = 0; sample < samples; ++sample) { tcu::Vec4 pixel; if (pixels.getFormat().type == tcu::TextureFormat::SIGNED_INT8 || pixels.getFormat().type == tcu::TextureFormat::UNSIGNED_INT8) { pixel = result[y * WIDTH + x * samples + sample]; } else { pixel = pixels.getPixel(x * samples + sample, y); } // Make sure only those samples where the sample mask bit is // non-zero have the "red" pixel values. if (!m_samples || (m_sampleMask & (1 << sample))) { if (pixel != tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f)) { isOk = false; } } else { if (pixel != tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)) { isOk = false; } } } } } gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); gl.deleteFramebuffers(1, &fbo); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); gl.deleteRenderbuffers(1, &rbo); gl.bindTexture(target, 0); gl.deleteTextures(1, &tex); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail"); return STOP; } class SampleShadingPositionCase : public deqp::TestCase { public: SampleShadingPositionCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLint samples, GLboolean fixedSampleLocations); ~SampleShadingPositionCase(); IterateResult iterate(); protected: glu::GLSLVersion m_glslVersion; GLint m_samples; GLboolean m_fixedSampleLocations; enum { WIDTH = 8, HEIGHT = 8, MAX_SAMPLES = 8, }; }; SampleShadingPositionCase::SampleShadingPositionCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLint samples, GLboolean fixedSampleLocations) : TestCase(context, name, description) , m_glslVersion(glslVersion) , m_samples(samples) , m_fixedSampleLocations(fixedSampleLocations) { DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion >= glu::GLSL_VERSION_400); } SampleShadingPositionCase::~SampleShadingPositionCase() { } SampleShadingPositionCase::IterateResult SampleShadingPositionCase::iterate() { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool isOk = true; /* OpenGL support query. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_es31_compatibility = m_context.getContextInfo().isExtensionSupported("GL_ARB_ES3_1_compatibility"); if (!(is_at_least_gl_45 || is_arb_es31_compatibility)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_ARB_ES3_1_compatibility"); return STOP; } GLint maxSamples; gl.getIntegerv(GL_MAX_SAMPLES, &maxSamples); if (m_samples > maxSamples) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Test sample count great than MAX_SAMPLES"); return STOP; } // Create a multisample texture, or a regular texture if samples is zero. GLuint tex; gl.genTextures(1, &tex); GLenum target; if (m_samples) { target = GL_TEXTURE_2D_MULTISAMPLE; gl.bindTexture(target, tex); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_samples, GL_RGBA8, WIDTH, HEIGHT, m_fixedSampleLocations); } else { target = GL_TEXTURE_2D; gl.bindTexture(target, tex); gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, WIDTH, HEIGHT); } // Attach the texture to the framebuffer to render to it. GLuint fboMs; gl.genFramebuffers(1, &fboMs); gl.bindFramebuffer(GL_FRAMEBUFFER, fboMs); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tex, 0); gl.viewport(0, 0, WIDTH, HEIGHT); // Save all the sample positions for this multisample framebuffer. std::vector samplePositions; if (m_samples) { samplePositions.resize(m_samples); for (int sample = 0; sample < m_samples; ++sample) { GLfloat position[2]; gl.getMultisamplefv(GL_SAMPLE_POSITION, sample, position); samplePositions[sample] = tcu::Vec4(position[0], position[1], 0.0f, 1.0f); } } static deUint16 const quadIndices[] = { 0, 1, 2, 2, 1, 3 }; { // Render all the sample positions to each pixel sample. static char const* vss = "${VERSION_DECL}\n" "in highp vec2 a_position;\n" "void main()\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; static char const* fss = "${VERSION_DECL}\n" "${OES_SV_RQ}" "layout(location = 0) out highp vec4 o_color;\n" "void main()\n" "{\n" " o_color = vec4(gl_SamplePosition, 0, 1);\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion).c_str(), specializeVersion(fss, m_glslVersion).c_str())); log << program; if (!program.isOk()) { TCU_FAIL("Compile failed"); } const float position[] = { -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, }; gl.useProgram(program.getProgram()); glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 2, 4, 0, &position[0]), }; glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw quad"); } gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); gl.deleteFramebuffers(1, &fboMs); // Create a regular non-multisample render buffer to resolve to multisample texture into. // The width is increased to save all samples of the pixel. GLsizei width = WIDTH * ((m_samples) ? m_samples : 1); GLuint rbo; gl.genRenderbuffers(1, &rbo); gl.bindRenderbuffer(GL_RENDERBUFFER, rbo); gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, HEIGHT); GLuint fbo; gl.genFramebuffers(1, &fbo); gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); gl.viewport(0, 0, width, HEIGHT); { // Resolve the multisample texture to the renderbuffer. static char const* vss = "${VERSION_DECL}\n" "in highp vec2 a_position;\n" "void main(void)\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; static char const* fss = "${VERSION_DECL}\n" "uniform highp sampler2D u_tex;\n" "uniform highp sampler2DMS u_texMS;\n" "uniform int u_samples;\n" "layout(location = 0) out highp vec4 o_color;\n" "void main(void)\n" "{\n" " if (u_samples > 0) {\n" " ivec2 coord = ivec2(int(gl_FragCoord.x) / u_samples, gl_FragCoord.y);\n" " int sampleId = int(gl_FragCoord.x) % u_samples;\n" " o_color = texelFetch(u_texMS, coord, sampleId);\n" " } else {\n" " ivec2 coord = ivec2(gl_FragCoord.x, gl_FragCoord.y);\n" " o_color = texelFetch(u_tex, coord, 0);\n" " }\n" "}\n"; glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(specializeVersion(vss, m_glslVersion).c_str(), specializeVersion(fss, m_glslVersion).c_str())); log << program; if (!program.isOk()) { TCU_FAIL("Compile failed"); } static float const position[] = { -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, }; gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_samples"), m_samples); if (m_samples > 0) { // only MS sampler needed, TU 1 is not used gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_tex"), 1); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_texMS"), 0); } else { // only non-MS sampler needed, TU 1 is not used gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_tex"), 0); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_texMS"), 1); } glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 2, 4, 0, &position[0]), }; glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw quad"); } // Read the renderbuffer pixels and verify we get back what we're expecting. tcu::TextureLevel results(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), width, HEIGHT); tcu::PixelBufferAccess pixels = results.getAccess(); glu::readPixels(m_context.getRenderContext(), 0, 0, pixels); if (m_samples) { // If m_fixedSampleLocations are used make sure the first pixel's samples // all match the SAMPLE_POSITION state saved earlier. std::set fixedSampleLocations; if (m_fixedSampleLocations) { for (int sample = 0; sample < m_samples; ++sample) { tcu::Vec4 pixel = pixels.getPixel(sample, 0); fixedSampleLocations.insert(pixel); if (deFloatAbs(pixel.x() - samplePositions[sample].x()) > 0.01 || deFloatAbs(pixel.y() - samplePositions[sample].y()) > 0.01) { isOk = false; } } } // Verify all samples of every pixel to make sure each position is unique. for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { std::set uniquePixels; for (int sample = 0; sample < m_samples; ++sample) { uniquePixels.insert(pixels.getPixel(x * m_samples + sample, y)); } if ((GLint)uniquePixels.size() != m_samples) { isOk = false; } // For the m_fixedSampleLocations case make sure each position // matches the sample positions of pixel(0, 0) saved earlier. if (m_fixedSampleLocations) { if (fixedSampleLocations != uniquePixels) { isOk = false; } } } } } else { // For the non-multisample case make sure all the positions are (0.5,0.5). for (int y = 0; y < pixels.getHeight(); ++y) { for (int x = 0; x < pixels.getWidth(); ++x) { tcu::Vec4 pixel = pixels.getPixel(x, y); if (deFloatAbs(pixel.x() - 0.5f) > 0.01 || deFloatAbs(pixel.y() - 0.5f) > 0.01 || pixel.z() != 0.0f || pixel.w() != 1.0f) { isOk = false; } } } } gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); gl.deleteFramebuffers(1, &fbo); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); gl.deleteRenderbuffers(1, &rbo); gl.bindTexture(target, 0); gl.deleteTextures(1, &tex); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail"); return STOP; } SampleVariablesTests::SampleVariablesTests(Context& context, glu::GLSLVersion glslVersion) : TestCaseGroup(context, "sample_variables", "Sample Variables tests"), m_glslVersion(glslVersion) { } SampleVariablesTests::~SampleVariablesTests() { } void SampleVariablesTests::init() { de::Random rnd(m_context.getTestContext().getCommandLine().getBaseSeed()); struct Sample { char const* name; GLint samples; } samples[] = { { "samples_0", 0 }, { "samples_1", 1 }, { "samples_2", 2 }, { "samples_4", 4 }, { "samples_8", 8 }, }; // sample_variables.extension if (m_glslVersion == glu::GLSL_VERSION_310_ES) { addChild(new SampleShadingExtensionCase(m_context, "extension", "#extension verification", m_glslVersion)); } // sample_variables.mask tcu::TestCaseGroup* maskGroup = new tcu::TestCaseGroup(m_testCtx, "mask", "gl_SampleMask tests"); addChild(maskGroup); struct Format { char const* name; GLenum internalFormat; tcu::TextureFormat textureFormat; char const* sampler; char const* outType; } formats[] = { { "rgba8", GL_RGBA8, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), "sampler2D", "vec4" }, { "rgba8i", GL_RGBA8I, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), "isampler2D", "ivec4" }, { "rgba8ui", GL_RGBA8UI, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), "usampler2D", "uvec4" }, { "rgba32f", GL_RGBA32F, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), "sampler2D", "vec4" }, }; for (int format = 0; format < DE_LENGTH_OF_ARRAY(formats); ++format) { tcu::TestCaseGroup* maskFormatGroup = new tcu::TestCaseGroup(m_testCtx, formats[format].name, ""); maskGroup->addChild(maskFormatGroup); for (int sample = 0; sample < DE_LENGTH_OF_ARRAY(samples); ++sample) { tcu::TestCaseGroup* maskFormatSampleGroup = new tcu::TestCaseGroup(m_testCtx, samples[sample].name, ""); maskFormatGroup->addChild(maskFormatSampleGroup); maskFormatSampleGroup->addChild( new SampleShadingMaskCase(m_context, "mask_zero", "", m_glslVersion, formats[format].internalFormat, formats[format].textureFormat, formats[format].sampler, formats[format].outType, samples[sample].samples, 0)); for (int mask = 0; mask < SAMPLE_MASKS; ++mask) { std::stringstream ss; ss << "mask_" << mask; maskFormatSampleGroup->addChild(new SampleShadingMaskCase( m_context, ss.str().c_str(), "", m_glslVersion, formats[format].internalFormat, formats[format].textureFormat, formats[format].sampler, formats[format].outType, samples[sample].samples, rnd.getUint32())); } } } // sample_variables.position tcu::TestCaseGroup* positionGroup = new tcu::TestCaseGroup(m_testCtx, "position", "gl_SamplePosition tests"); addChild(positionGroup); struct Fixed { char const* name; GLboolean fixedSampleLocations; } fixed[] = { { "non-fixed", GL_FALSE }, { "fixed", GL_TRUE }, }; for (int j = 0; j < DE_LENGTH_OF_ARRAY(fixed); ++j) { tcu::TestCaseGroup* positionFixedGroup = new tcu::TestCaseGroup(m_testCtx, fixed[j].name, ""); positionGroup->addChild(positionFixedGroup); for (int sample = 0; sample < DE_LENGTH_OF_ARRAY(samples); ++sample) { positionFixedGroup->addChild(new SampleShadingPositionCase(m_context, samples[sample].name, "", m_glslVersion, samples[sample].samples, fixed[j].fixedSampleLocations)); } } } } // es31compatibility } // gl4cts