/*------------------------------------------------------------------------- * 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 "es31cSampleShadingTests.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "gluContextInfo.hpp" #include "gluDrawUtil.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "glw.h" #include "glwFunctions.hpp" #include "tcuCommandLine.hpp" #include "tcuStringTemplate.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" namespace 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 glcts { using tcu::TestLog; using std::string; using std::vector; using glcts::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; return tcu::StringTemplate(source.c_str()).specialize(args); } class SampleShadingApiCaseGroup : public TestCaseGroup { public: SampleShadingApiCaseGroup(Context& context, glu::GLSLVersion glslVersion) : TestCaseGroup(context, "api", "Basic API verification"), m_glslVersion(glslVersion) { } void init(void) { addChild(new SampleShadingApiCase(m_context, m_glslVersion)); } private: class SampleShadingApiCase : public deqp::TestCase { public: SampleShadingApiCase(Context& context, glu::GLSLVersion glslVersion); ~SampleShadingApiCase(); IterateResult iterate(); protected: glu::GLSLVersion m_glslVersion; glw::glMinSampleShadingFunc m_pGLMinSampleShading; }; glu::GLSLVersion m_glslVersion; }; SampleShadingApiCaseGroup::SampleShadingApiCase::SampleShadingApiCase(Context& context, glu::GLSLVersion glslVersion) : TestCase(context, "verify", ""), m_glslVersion(glslVersion), m_pGLMinSampleShading(0) { DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion >= glu::GLSL_VERSION_400); } SampleShadingApiCaseGroup::SampleShadingApiCase::~SampleShadingApiCase() { } SampleShadingApiCaseGroup::SampleShadingApiCase::IterateResult SampleShadingApiCaseGroup::SampleShadingApiCase:: iterate() { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool isOk = true; if (m_glslVersion == glu::GLSL_VERSION_310_ES && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_OES_sample_shading"); return STOP; } m_pGLMinSampleShading = gl.minSampleShading; struct Test { GLfloat input; GLfloat result; } tests[] = { { 0.0f, 0.0f }, { 0.5f, 0.5f }, { -1.0f, 0.0f }, { 2.0f, 1.0f }, }; for (int i = 0; i < DE_LENGTH_OF_ARRAY(tests); ++i) { m_pGLMinSampleShading(tests[i].input); GLfloat result = -1.0f; gl.getFloatv(GL_MIN_SAMPLE_SHADING_VALUE_OES, &result); if (result != tests[i].result) { isOk = false; } } gl.enable(GL_SAMPLE_SHADING_OES); if (!gl.isEnabled(GL_SAMPLE_SHADING_OES)) { isOk = false; } gl.disable(GL_SAMPLE_SHADING_OES); if (gl.isEnabled(GL_SAMPLE_SHADING_OES)) { isOk = false; } m_pGLMinSampleShading(0.0f); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail"); return STOP; } class SampleShadingRenderCase : public TestCase { public: SampleShadingRenderCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLenum internalFormat, tcu::TextureFormat const& texFormat, const char* m_sampler, const char* m_outType, GLfloat min, GLfloat max, const char* m_extension, GLfloat sampleShading); ~SampleShadingRenderCase(); IterateResult iterate(); protected: glu::GLSLVersion m_glslVersion; glw::glMinSampleShadingFunc m_pGLMinSampleShading; GLenum m_internalFormat; tcu::TextureFormat m_texFormat; std::string m_sampler; std::string m_outType; GLfloat m_min; GLfloat m_max; std::string m_extension; GLfloat m_sampleShading; enum { WIDTH = 16, HEIGHT = 16, MAX_SAMPLES = 4, }; int countUniquePixels(tcu::ConstPixelBufferAccess const& pixels); int countUniquePixels(const std::vector& pixels); }; SampleShadingRenderCase::SampleShadingRenderCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion, GLenum internalFormat, tcu::TextureFormat const& texFormat, const char* sampler, const char* outType, GLfloat min, GLfloat max, const char* extension, GLfloat sampleShading) : TestCase(context, name, description) , m_glslVersion(glslVersion) , m_pGLMinSampleShading(0) , m_internalFormat(internalFormat) , m_texFormat(texFormat) , m_sampler(sampler) , m_outType(outType) , m_min(min) , m_max(max) , m_extension(extension) , m_sampleShading(sampleShading) { DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion >= glu::GLSL_VERSION_400); } SampleShadingRenderCase::~SampleShadingRenderCase() { } SampleShadingRenderCase::IterateResult SampleShadingRenderCase::iterate() { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool isOk = true; if (m_glslVersion == glu::GLSL_VERSION_310_ES && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_OES_sample_shading"); return STOP; } if (!m_extension.empty() && !m_context.getContextInfo().isExtensionSupported(m_extension.c_str())) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, m_extension.c_str()); return STOP; } m_pGLMinSampleShading = gl.minSampleShading; GLint maxSamples = 0; 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 (maxSamples == 0) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Multisample is not supported on this format"); 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); } else { gl.getIntegerv(GL_MAX_SAMPLES, &maxSamples); } GLint samples = de::min(maxSamples, MAX_SAMPLES); GLuint tex; gl.genTextures(1, &tex); gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex); gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, m_internalFormat, WIDTH, HEIGHT, GL_FALSE); GLuint fboMs; gl.genFramebuffers(1, &fboMs); gl.bindFramebuffer(GL_FRAMEBUFFER, fboMs); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0); gl.viewport(0, 0, WIDTH, HEIGHT); m_pGLMinSampleShading(m_sampleShading); gl.enable(GL_SAMPLE_SHADING_OES); static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 }; { static char const* vss = "${VERSION_DECL}\n" "in highp vec2 a_position;\n" "in highp vec4 a_color;\n" "out highp vec4 v_color;\n" "void main (void)\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" " v_color = a_color;\n" "}\n"; static char const* fss = "${VERSION_DECL}\n" "in highp vec4 v_color;\n" "layout(location = 0) out highp ${OUT_TYPE} o_color;\n" "void main (void)\n" "{\n" " o_color = ${OUT_TYPE}(v_color.x, v_color.y, 0.0, 0.0);\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"); } const float position[] = { -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f, }; const float color[] = { m_min, m_min, 0.0f, 1.0f, m_min, m_max, 0.0f, 1.0f, m_max, m_min, 0.0f, 1.0f, m_max, m_max, 0.0f, 1.0f, }; gl.useProgram(program.getProgram()); glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 2, 4, 0, &position[0]), glu::va::Float("a_color", 4, 4, 0, &color[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"); } m_pGLMinSampleShading(0.0f); gl.disable(GL_SAMPLE_SHADING_OES); gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); gl.deleteFramebuffers(1, &fboMs); GLsizei width = WIDTH * samples; 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); { 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 int u_samples;\n" "layout(location = 0) out highp ${OUT_TYPE} o_color;\n" "void main(void)\n" "{\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_tex, coord, sampleId);\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"); } 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"), samples); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_tex"), 0); 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 results(m_texFormat, width, HEIGHT); tcu::PixelBufferAccess pixels = results.getAccess(); std::vector result(pixels.getHeight() * pixels.getWidth()); int uniquePixels; 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]); } uniquePixels = countUniquePixels(result); } 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]); } uniquePixels = countUniquePixels(result); } else { glu::readPixels(m_context.getRenderContext(), 0, 0, pixels); uniquePixels = countUniquePixels(pixels); } int expectedUnique = WIDTH * HEIGHT * (de::clamp(int(float(samples) * m_sampleShading), 1, samples)); if (uniquePixels < expectedUnique) { 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(GL_TEXTURE_2D_MULTISAMPLE, 0); gl.deleteTextures(1, &tex); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail"); return STOP; } int SampleShadingRenderCase::countUniquePixels(tcu::ConstPixelBufferAccess const& pixels) { std::set uniquePixels; for (int y = 0; y < pixels.getHeight(); ++y) { for (int x = 0; x < pixels.getWidth(); ++x) { uniquePixels.insert(pixels.getPixel(x, y)); } } return (int)uniquePixels.size(); } int SampleShadingRenderCase::countUniquePixels(const std::vector& pixels) { std::set uniquePixels; for (unsigned int i = 0; i < pixels.size(); ++i) { uniquePixels.insert(pixels[i]); } return (int)uniquePixels.size(); } class SampleShadingRenderFormatTests : public glcts::TestCaseGroup { public: SampleShadingRenderFormatTests(glcts::Context& context, glu::GLSLVersion glslVersion, GLenum internalFormat, const char* format, tcu::TextureFormat const& texFormat, const char* sampler, const char* outType, GLfloat min, GLfloat max, const char* extension = ""); ~SampleShadingRenderFormatTests(void); void init(void); private: SampleShadingRenderFormatTests(const SampleShadingTests& other); SampleShadingRenderFormatTests& operator=(const SampleShadingTests& other); glu::GLSLVersion m_glslVersion; GLenum m_internalFormat; tcu::TextureFormat m_texFormat; std::string m_sampler; std::string m_outType; GLfloat m_min; GLfloat m_max; std::string m_extension; }; SampleShadingRenderFormatTests::SampleShadingRenderFormatTests(Context& context, glu::GLSLVersion glslVersion, GLenum internalFormat, const char* format, tcu::TextureFormat const& texFormat, const char* sampler, const char* outType, GLfloat min, GLfloat max, const char* extension) : TestCaseGroup(context, format, "") , m_glslVersion(glslVersion) , m_internalFormat(internalFormat) , m_texFormat(texFormat) , m_sampler(sampler) , m_outType(outType) , m_min(min) , m_max(max) , m_extension(extension) { } SampleShadingRenderFormatTests::~SampleShadingRenderFormatTests(void) { } void SampleShadingRenderFormatTests::init(void) { // sample_shading.render.full addChild(new SampleShadingRenderCase(m_context, "full", "Sample shader functionality", m_glslVersion, m_internalFormat, m_texFormat, m_sampler.c_str(), m_outType.c_str(), m_min, m_max, m_extension.c_str(), 1.0)); // sample_shading.render.half addChild(new SampleShadingRenderCase(m_context, "half", "Sample shader functionality", m_glslVersion, m_internalFormat, m_texFormat, m_sampler.c_str(), m_outType.c_str(), m_min, m_max, m_extension.c_str(), 0.5)); // sample_shading.render.none addChild(new SampleShadingRenderCase(m_context, "none", "Sample shader functionality", m_glslVersion, m_internalFormat, m_texFormat, m_sampler.c_str(), m_outType.c_str(), m_min, m_max, m_extension.c_str(), 0.0)); } class SampleShadingRenderTests : public glcts::TestCaseGroup { public: SampleShadingRenderTests(glcts::Context& context, glu::GLSLVersion glslVersion); ~SampleShadingRenderTests(void); void init(void); private: SampleShadingRenderTests(const SampleShadingTests& other); SampleShadingRenderTests& operator=(const SampleShadingTests& other); glu::GLSLVersion m_glslVersion; }; SampleShadingRenderTests::SampleShadingRenderTests(Context& context, glu::GLSLVersion glslVersion) : TestCaseGroup(context, "render", "Sample Shading render tests"), m_glslVersion(glslVersion) { } SampleShadingRenderTests::~SampleShadingRenderTests(void) { } void SampleShadingRenderTests::init(void) { // sample_shading.render.rgba8 addChild(new SampleShadingRenderFormatTests( m_context, m_glslVersion, GL_RGBA8, "rgba8", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), "sampler2DMS", "vec4", 0.0, 1.0)); // sample_shading.render.rgba8i addChild(new SampleShadingRenderFormatTests( m_context, m_glslVersion, GL_RGBA8I, "rgba8i", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), "isampler2DMS", "ivec4", -128.0, 127.0)); // sample_shading.render.rgba8ui addChild(new SampleShadingRenderFormatTests( m_context, m_glslVersion, GL_RGBA8UI, "rgba8ui", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), "usampler2DMS", "uvec4", 0.0, 255.0)); // sample_shading.render.rgba32f const char* extension = (glu::isContextTypeES(m_context.getRenderContext().getType())) ? "GL_EXT_color_buffer_float" : ""; addChild(new SampleShadingRenderFormatTests(m_context, m_glslVersion, GL_RGBA32F, "rgba32f", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), "sampler2DMS", "vec4", 0.0, 1.0, extension)); } SampleShadingTests::SampleShadingTests(Context& context, glu::GLSLVersion glslVersion) : TestCaseGroup(context, "sample_shading", "Sample Shading tests"), m_glslVersion(glslVersion) { } SampleShadingTests::~SampleShadingTests(void) { } void SampleShadingTests::init(void) { // sample_shading.api addChild(new SampleShadingApiCaseGroup(m_context, m_glslVersion)); // sample_shading.render addChild(new SampleShadingRenderTests(m_context, m_glslVersion)); } } // glcts