| /*------------------------------------------------------------------------- |
| * 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 "glcSampleVariablesTests.hpp" |
| #include "deMath.h" |
| #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 deqp |
| { |
| |
| using tcu::TestLog; |
| using std::string; |
| using std::vector; |
| |
| 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<std::string, std::string> 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 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(); |
| |
| if (!m_context.getContextInfo().isExtensionSupported("GL_OES_sample_variables")) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_OES_sample_variables"); |
| 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 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; |
| bool supportsRgba32f = false; |
| |
| if (m_glslVersion == glu::GLSL_VERSION_310_ES && |
| !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_variables")) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_OES_sample_variables"); |
| return STOP; |
| } |
| |
| supportsRgba32f = isContextTypeGLCore(m_context.getRenderContext().getType()) ? |
| true : |
| (m_context.getContextInfo().isExtensionSupported("GL_EXT_color_buffer_float") || |
| m_context.getContextInfo().isExtensionSupported("GL_ARB_color_buffer_float")); |
| |
| if (m_internalFormat == GL_RGBA32F && !supportsRgba32f) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Internalformat rgba32f not supported"); |
| 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 results(m_texFormat, width, HEIGHT); |
| tcu::PixelBufferAccess pixels = results.getAccess(); |
| std::vector<tcu::Vec4> result(pixels.getHeight() * pixels.getWidth()); |
| |
| if (pixels.getFormat().type == tcu::TextureFormat::SIGNED_INT8) |
| { |
| std::vector<GLint> 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<GLuint> 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 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; |
| |
| if (m_glslVersion == glu::GLSL_VERSION_310_ES && |
| !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_variables")) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_OES_sample_variables"); |
| 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<tcu::Vec4> 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<tcu::Vec4> 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<tcu::Vec4> 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) |
| { |
| tcu::TestCaseGroup* extensionCaseGroup = new tcu::TestCaseGroup(m_testCtx, "verification", ""); |
| extensionCaseGroup->addChild( |
| new SampleShadingExtensionCase(m_context, "extension", "#extension verification", m_glslVersion)); |
| addChild(extensionCaseGroup); |
| } |
| |
| // 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)); |
| } |
| } |
| } |
| |
| } // glcts |