/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * 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 Multisample texture test *//*--------------------------------------------------------------------*/ #include "es31fTextureMultisampleTests.hpp" #include "tcuTestLog.hpp" #include "tcuRenderTarget.hpp" #include "tcuSurface.hpp" #include "tcuStringTemplate.hpp" #include "tcuTextureUtil.hpp" #include "glsStateQueryUtil.hpp" #include "glsRasterizationTestUtil.hpp" #include "gluRenderContext.hpp" #include "gluCallLogWrapper.hpp" #include "gluObjectWrapper.hpp" #include "gluShaderProgram.hpp" #include "gluPixelTransfer.hpp" #include "gluStrUtil.hpp" #include "gluContextInfo.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "deStringUtil.hpp" #include "deRandom.hpp" using namespace glw; using deqp::gls::RasterizationTestUtil::RasterizationArguments; using deqp::gls::RasterizationTestUtil::TriangleSceneSpec; namespace deqp { namespace gles31 { namespace Functional { namespace { static std::string sampleMaskToString (const std::vector<deUint32>& bitfield, int numBits) { std::string result(numBits, '0'); // move from back to front and set chars to 1 for (int wordNdx = 0; wordNdx < (int)bitfield.size(); ++wordNdx) { for (int bit = 0; bit < 32; ++bit) { const int targetCharNdx = numBits - (wordNdx*32+bit) - 1; // beginning of the string reached if (targetCharNdx < 0) return result; if ((bitfield[wordNdx] >> bit) & 0x01) result[targetCharNdx] = '1'; } } return result; } /*--------------------------------------------------------------------*//*! * \brief Returns the number of words needed to represent mask of given length *//*--------------------------------------------------------------------*/ static int getEffectiveSampleMaskWordCount (int highestBitNdx) { const int wordSize = 32; const int maskLen = highestBitNdx + 1; return ((maskLen - 1) / wordSize) + 1; // round_up(mask_len / wordSize) } /*--------------------------------------------------------------------*//*! * \brief Creates sample mask with all less significant bits than nthBit set *//*--------------------------------------------------------------------*/ static std::vector<deUint32> genAllSetToNthBitSampleMask (int nthBit) { const int wordSize = 32; const int numWords = getEffectiveSampleMaskWordCount(nthBit - 1); const deUint32 topWordBits = (deUint32)(nthBit - (numWords - 1) * wordSize); std::vector<deUint32> mask (numWords); for (int ndx = 0; ndx < numWords - 1; ++ndx) mask[ndx] = 0xFFFFFFFF; mask[numWords - 1] = (deUint32)((1ULL << topWordBits) - (deUint32)1); return mask; } /*--------------------------------------------------------------------*//*! * \brief Creates sample mask with nthBit set *//*--------------------------------------------------------------------*/ static std::vector<deUint32> genSetNthBitSampleMask (int nthBit) { const int wordSize = 32; const int numWords = getEffectiveSampleMaskWordCount(nthBit); const deUint32 topWordBits = (deUint32)(nthBit - (numWords - 1) * wordSize); std::vector<deUint32> mask (numWords); for (int ndx = 0; ndx < numWords - 1; ++ndx) mask[ndx] = 0; mask[numWords - 1] = (deUint32)(1ULL << topWordBits); return mask; } class SamplePosRasterizationTest : public TestCase { public: SamplePosRasterizationTest (Context& context, const char* name, const char* desc, int samples); ~SamplePosRasterizationTest (void); private: void init (void); void deinit (void); IterateResult iterate (void); void genMultisampleTexture (void); void genSamplerProgram (void); bool testMultisampleTexture (int sampleNdx); void drawSample (tcu::Surface& dst, int sampleNdx); void convertToSceneSpec (TriangleSceneSpec& scene, const tcu::Vec2& samplePos) const; struct Triangle { tcu::Vec4 p1; tcu::Vec4 p2; tcu::Vec4 p3; }; const int m_samples; const int m_canvasSize; std::vector<Triangle> m_testTriangles; int m_iteration; bool m_allIterationsOk; GLuint m_texID; GLuint m_vaoID; GLuint m_vboID; std::vector<tcu::Vec2> m_samplePositions; int m_subpixelBits; const glu::ShaderProgram* m_samplerProgram; GLint m_samplerProgramPosLoc; GLint m_samplerProgramSamplerLoc; GLint m_samplerProgramSampleNdxLoc; }; SamplePosRasterizationTest::SamplePosRasterizationTest (Context& context, const char* name, const char* desc, int samples) : TestCase (context, name, desc) , m_samples (samples) , m_canvasSize (256) , m_iteration (0) , m_allIterationsOk (true) , m_texID (0) , m_vaoID (0) , m_vboID (0) , m_subpixelBits (0) , m_samplerProgram (DE_NULL) , m_samplerProgramPosLoc (-1) , m_samplerProgramSamplerLoc (-1) , m_samplerProgramSampleNdxLoc (-1) { } SamplePosRasterizationTest::~SamplePosRasterizationTest (void) { deinit(); } void SamplePosRasterizationTest::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); GLint maxSamples = 0; // requirements if (m_context.getRenderTarget().getWidth() < m_canvasSize || m_context.getRenderTarget().getHeight() < m_canvasSize) throw tcu::NotSupportedError("render target size must be at least " + de::toString(m_canvasSize) + "x" + de::toString(m_canvasSize)); gl.getIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &maxSamples); if (m_samples > maxSamples) throw tcu::NotSupportedError("Requested sample count is greater than GL_MAX_COLOR_TEXTURE_SAMPLES"); m_testCtx.getLog() << tcu::TestLog::Message << "GL_MAX_COLOR_TEXTURE_SAMPLES = " << maxSamples << tcu::TestLog::EndMessage; gl.getIntegerv(GL_SUBPIXEL_BITS, &m_subpixelBits); m_testCtx.getLog() << tcu::TestLog::Message << "GL_SUBPIXEL_BITS = " << m_subpixelBits << tcu::TestLog::EndMessage; // generate textures & other gl stuff m_testCtx.getLog() << tcu::TestLog::Message << "Creating multisample texture" << tcu::TestLog::EndMessage; gl.genTextures (1, &m_texID); gl.bindTexture (GL_TEXTURE_2D_MULTISAMPLE, m_texID); gl.texStorage2DMultisample (GL_TEXTURE_2D_MULTISAMPLE, m_samples, GL_RGBA8, m_canvasSize, m_canvasSize, GL_TRUE); GLU_EXPECT_NO_ERROR (gl.getError(), "texStorage2DMultisample"); gl.genVertexArrays (1, &m_vaoID); gl.bindVertexArray (m_vaoID); GLU_EXPECT_NO_ERROR (gl.getError(), "bindVertexArray"); gl.genBuffers (1, &m_vboID); gl.bindBuffer (GL_ARRAY_BUFFER, m_vboID); GLU_EXPECT_NO_ERROR (gl.getError(), "bindBuffer"); // generate test scene for (int i = 0; i < 20; ++i) { // vertical spikes Triangle tri; tri.p1 = tcu::Vec4(((float)i + 1.0f / (float)(i + 1)) / 20.0f, 0.0f, 0.0f, 1.0f); tri.p2 = tcu::Vec4(((float)i + 0.3f + 1.0f / (float)(i + 1)) / 20.0f, 0.0f, 0.0f, 1.0f); tri.p3 = tcu::Vec4(((float)i + 1.0f / (float)(i + 1)) / 20.0f, -1.0f, 0.0f, 1.0f); m_testTriangles.push_back(tri); } for (int i = 0; i < 20; ++i) { // horisontal spikes Triangle tri; tri.p1 = tcu::Vec4(-1.0f, ((float)i + 1.0f / (float)(i + 1)) / 20.0f, 0.0f, 1.0f); tri.p2 = tcu::Vec4(-1.0f, ((float)i + 0.3f + 1.0f / (float)(i + 1)) / 20.0f, 0.0f, 1.0f); tri.p3 = tcu::Vec4( 0.0f, ((float)i + 1.0f / (float)(i + 1)) / 20.0f, 0.0f, 1.0f); m_testTriangles.push_back(tri); } for (int i = 0; i < 20; ++i) { // fan const tcu::Vec2 p = tcu::Vec2(deFloatCos(((float)i)/20.0f*DE_PI*2) * 0.5f + 0.5f, deFloatSin(((float)i)/20.0f*DE_PI*2) * 0.5f + 0.5f); const tcu::Vec2 d = tcu::Vec2(0.1f, 0.02f); Triangle tri; tri.p1 = tcu::Vec4(0.4f, 0.4f, 0.0f, 1.0f); tri.p2 = tcu::Vec4(p.x(), p.y(), 0.0f, 1.0f); tri.p3 = tcu::Vec4(p.x() + d.x(), p.y() + d.y(), 0.0f, 1.0f); m_testTriangles.push_back(tri); } { Triangle tri; tri.p1 = tcu::Vec4(-0.202f, -0.202f, 0.0f, 1.0f); tri.p2 = tcu::Vec4(-0.802f, -0.202f, 0.0f, 1.0f); tri.p3 = tcu::Vec4(-0.802f, -0.802f, 0.0f, 1.0f); m_testTriangles.push_back(tri); } // generate multisample texture (and query the sample positions in it) genMultisampleTexture(); // verify queried samples are in a valid range for (int sampleNdx = 0; sampleNdx < m_samples; ++sampleNdx) { if (m_samplePositions[sampleNdx].x() < 0.0f || m_samplePositions[sampleNdx].x() > 1.0f || m_samplePositions[sampleNdx].y() < 0.0f || m_samplePositions[sampleNdx].y() > 1.0f) { m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Sample position of sample " << sampleNdx << " should be in range ([0, 1], [0, 1]). Got " << m_samplePositions[sampleNdx] << tcu::TestLog::EndMessage; throw tcu::TestError("invalid sample position"); } } // generate sampler program genSamplerProgram(); } void SamplePosRasterizationTest::deinit (void) { if (m_vboID) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vboID); m_vboID = 0; } if (m_vaoID) { m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vaoID); m_vaoID = 0; } if (m_texID) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texID); m_texID = 0; } if (m_samplerProgram) { delete m_samplerProgram; m_samplerProgram = DE_NULL; } } SamplePosRasterizationTest::IterateResult SamplePosRasterizationTest::iterate (void) { m_allIterationsOk &= testMultisampleTexture(m_iteration); m_iteration++; if (m_iteration < m_samples) return CONTINUE; // End result if (m_allIterationsOk) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Pixel comparison failed"); return STOP; } void SamplePosRasterizationTest::genMultisampleTexture (void) { const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; const char* const fragmentShaderSource = "#version 310 es\n" "layout(location = 0) out highp vec4 fragColor;\n" "void main (void)\n" "{\n" " fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" "}\n"; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(fragmentShaderSource)); const GLuint posLoc = gl.getAttribLocation(program.getProgram(), "a_position"); GLuint fboID = 0; if (!program.isOk()) { m_testCtx.getLog() << program; throw tcu::TestError("Failed to build shader."); } gl.bindTexture (GL_TEXTURE_2D_MULTISAMPLE, m_texID); gl.bindVertexArray (m_vaoID); gl.bindBuffer (GL_ARRAY_BUFFER, m_vboID); // Setup fbo for drawing and for sample position query m_testCtx.getLog() << tcu::TestLog::Message << "Attaching texture to FBO" << tcu::TestLog::EndMessage; gl.genFramebuffers (1, &fboID); gl.bindFramebuffer (GL_FRAMEBUFFER, fboID); gl.framebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_texID, 0); GLU_EXPECT_NO_ERROR (gl.getError(), "framebufferTexture2D"); // Query sample positions of the multisample texture by querying the sample positions // from an fbo which has the multisample texture as attachment. m_testCtx.getLog() << tcu::TestLog::Message << "Sample locations:" << tcu::TestLog::EndMessage; for (int sampleNdx = 0; sampleNdx < m_samples; ++sampleNdx) { gls::StateQueryUtil::StateQueryMemoryWriteGuard<float[2]> position; gl.getMultisamplefv(GL_SAMPLE_POSITION, (deUint32)sampleNdx, position); if (!position.verifyValidity(m_testCtx)) throw tcu::TestError("Error while querying sample positions"); m_testCtx.getLog() << tcu::TestLog::Message << "\t" << sampleNdx << ": (" << position[0] << ", " << position[1] << ")" << tcu::TestLog::EndMessage; m_samplePositions.push_back(tcu::Vec2(position[0], position[1])); } // Draw test pattern to texture m_testCtx.getLog() << tcu::TestLog::Message << "Drawing test pattern to the texture" << tcu::TestLog::EndMessage; gl.bufferData (GL_ARRAY_BUFFER, (glw::GLsizeiptr)(m_testTriangles.size() * sizeof(Triangle)), &m_testTriangles[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR (gl.getError(), "bufferData"); gl.viewport (0, 0, m_canvasSize, m_canvasSize); gl.clearColor (0, 0, 0, 1); gl.clear (GL_COLOR_BUFFER_BIT); gl.vertexAttribPointer (posLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray (posLoc); GLU_EXPECT_NO_ERROR (gl.getError(), "vertexAttribPointer"); gl.useProgram (program.getProgram()); gl.drawArrays (GL_TRIANGLES, 0, (glw::GLsizei)(m_testTriangles.size() * 3)); GLU_EXPECT_NO_ERROR (gl.getError(), "drawArrays"); gl.disableVertexAttribArray (posLoc); gl.useProgram (0); gl.deleteFramebuffers (1, &fboID); GLU_EXPECT_NO_ERROR (gl.getError(), "cleanup"); } void SamplePosRasterizationTest::genSamplerProgram (void) { const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; const char* const fragShaderSource = "#version 310 es\n" "layout(location = 0) out highp vec4 fragColor;\n" "uniform highp sampler2DMS u_sampler;\n" "uniform highp int u_sample;\n" "void main (void)\n" "{\n" " fragColor = texelFetch(u_sampler, ivec2(int(floor(gl_FragCoord.x)), int(floor(gl_FragCoord.y))), u_sample);\n" "}\n"; const tcu::ScopedLogSection section (m_testCtx.getLog(), "Generate sampler shader", "Generate sampler shader"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_samplerProgram = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(fragShaderSource)); m_testCtx.getLog() << *m_samplerProgram; if (!m_samplerProgram->isOk()) throw tcu::TestError("Could not create sampler program."); m_samplerProgramPosLoc = gl.getAttribLocation(m_samplerProgram->getProgram(), "a_position"); m_samplerProgramSamplerLoc = gl.getUniformLocation(m_samplerProgram->getProgram(), "u_sampler"); m_samplerProgramSampleNdxLoc = gl.getUniformLocation(m_samplerProgram->getProgram(), "u_sample"); } bool SamplePosRasterizationTest::testMultisampleTexture (int sampleNdx) { tcu::Surface glSurface(m_canvasSize, m_canvasSize); TriangleSceneSpec scene; // Draw sample drawSample(glSurface, sampleNdx); // Draw reference(s) convertToSceneSpec(scene, m_samplePositions[sampleNdx]); // Compare { RasterizationArguments args; args.redBits = m_context.getRenderTarget().getPixelFormat().redBits; args.greenBits = m_context.getRenderTarget().getPixelFormat().greenBits; args.blueBits = m_context.getRenderTarget().getPixelFormat().blueBits; args.numSamples = 0; args.subpixelBits = m_subpixelBits; return gls::RasterizationTestUtil::verifyTriangleGroupRasterization(glSurface, scene, args, m_testCtx.getLog(), deqp::gls::RasterizationTestUtil::VERIFICATIONMODE_STRICT); } } void SamplePosRasterizationTest::drawSample (tcu::Surface& dst, int sampleNdx) { // Downsample using only one sample static const tcu::Vec4 fullscreenQuad[] = { tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f) }; const tcu::ScopedLogSection section (m_testCtx.getLog(), "Test sample position " + de::toString(sampleNdx+1) + "/" + de::toString(m_samples), "Test sample position " + de::toString(sampleNdx+1) + "/" + de::toString(m_samples)); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindTexture (GL_TEXTURE_2D_MULTISAMPLE, m_texID); gl.bindVertexArray (m_vaoID); gl.bindBuffer (GL_ARRAY_BUFFER, m_vboID); gl.bufferData (GL_ARRAY_BUFFER, sizeof(fullscreenQuad), &fullscreenQuad[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR (gl.getError(), "bufferData"); gl.viewport (0, 0, m_canvasSize, m_canvasSize); gl.clearColor (0, 0, 0, 1); gl.clear (GL_COLOR_BUFFER_BIT); gl.vertexAttribPointer (m_samplerProgramPosLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray (m_samplerProgramPosLoc); GLU_EXPECT_NO_ERROR (gl.getError(), "vertexAttribPointer"); gl.useProgram (m_samplerProgram->getProgram()); gl.uniform1i (m_samplerProgramSamplerLoc, 0); gl.uniform1i (m_samplerProgramSampleNdxLoc, (deInt32)sampleNdx); GLU_EXPECT_NO_ERROR (gl.getError(), "useprogram"); m_testCtx.getLog() << tcu::TestLog::Message << "Reading from texture with sample index " << sampleNdx << tcu::TestLog::EndMessage; gl.drawArrays (GL_TRIANGLE_STRIP, 0, 4); GLU_EXPECT_NO_ERROR (gl.getError(), "drawArrays"); gl.disableVertexAttribArray (m_samplerProgramPosLoc); gl.useProgram (0); GLU_EXPECT_NO_ERROR (gl.getError(), "cleanup"); gl.finish (); glu::readPixels (m_context.getRenderContext(), 0, 0, dst.getAccess()); GLU_EXPECT_NO_ERROR (gl.getError(), "readPixels"); } void SamplePosRasterizationTest::convertToSceneSpec (TriangleSceneSpec& scene, const tcu::Vec2& samplePos) const { // Triangles are offset from the pixel center by "offset". Move the triangles back to take this into account. const tcu::Vec4 offset = tcu::Vec4(samplePos.x() - 0.5f, samplePos.y() - 0.5f, 0.0f, 0.0f) / tcu::Vec4((float)m_canvasSize, (float)m_canvasSize, 1.0f, 1.0f) * 2.0f; for (int triangleNdx = 0; triangleNdx < (int)m_testTriangles.size(); ++triangleNdx) { TriangleSceneSpec::SceneTriangle triangle; triangle.positions[0] = m_testTriangles[triangleNdx].p1 - offset; triangle.positions[1] = m_testTriangles[triangleNdx].p2 - offset; triangle.positions[2] = m_testTriangles[triangleNdx].p3 - offset; triangle.sharedEdge[0] = false; triangle.sharedEdge[1] = false; triangle.sharedEdge[2] = false; scene.triangles.push_back(triangle); } } class SampleMaskCase : public TestCase { public: enum CaseFlags { FLAGS_NONE = 0, FLAGS_ALPHA_TO_COVERAGE = (1ULL << 0), FLAGS_SAMPLE_COVERAGE = (1ULL << 1), FLAGS_HIGH_BITS = (1ULL << 2), }; SampleMaskCase (Context& context, const char* name, const char* desc, int samples, int flags); ~SampleMaskCase (void); private: void init (void); void deinit (void); IterateResult iterate (void); void genSamplerProgram (void); void genAlphaProgram (void); void updateTexture (int sample); bool verifyTexture (int sample); void drawSample (tcu::Surface& dst, int sample); const int m_samples; const int m_canvasSize; const int m_gridsize; const int m_effectiveSampleMaskWordCount; int m_flags; int m_currentSample; int m_allIterationsOk; glw::GLuint m_texID; glw::GLuint m_vaoID; glw::GLuint m_vboID; glw::GLuint m_fboID; const glu::ShaderProgram* m_samplerProgram; glw::GLint m_samplerProgramPosLoc; glw::GLint m_samplerProgramSamplerLoc; glw::GLint m_samplerProgramSampleNdxLoc; const glu::ShaderProgram* m_alphaProgram; glw::GLint m_alphaProgramPosLoc; }; SampleMaskCase::SampleMaskCase (Context& context, const char* name, const char* desc, int samples, int flags) : TestCase (context, name, desc) , m_samples (samples) , m_canvasSize (256) , m_gridsize (16) , m_effectiveSampleMaskWordCount(getEffectiveSampleMaskWordCount(samples - 1)) , m_flags (flags) , m_currentSample (-1) , m_allIterationsOk (true) , m_texID (0) , m_vaoID (0) , m_vboID (0) , m_fboID (0) , m_samplerProgram (DE_NULL) , m_samplerProgramPosLoc (-1) , m_samplerProgramSamplerLoc (-1) , m_samplerProgramSampleNdxLoc (-1) , m_alphaProgram (DE_NULL) , m_alphaProgramPosLoc (-1) { } SampleMaskCase::~SampleMaskCase (void) { deinit(); } void SampleMaskCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint maxSamples = 0; glw::GLint maxSampleMaskWords = 0; // requirements if (m_context.getRenderTarget().getWidth() < m_canvasSize || m_context.getRenderTarget().getHeight() < m_canvasSize) throw tcu::NotSupportedError("render target size must be at least " + de::toString(m_canvasSize) + "x" + de::toString(m_canvasSize)); gl.getIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords); if (m_effectiveSampleMaskWordCount > maxSampleMaskWords) throw tcu::NotSupportedError("Test requires larger GL_MAX_SAMPLE_MASK_WORDS"); gl.getIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &maxSamples); if (m_samples > maxSamples) throw tcu::NotSupportedError("Requested sample count is greater than GL_MAX_COLOR_TEXTURE_SAMPLES"); m_testCtx.getLog() << tcu::TestLog::Message << "GL_MAX_COLOR_TEXTURE_SAMPLES = " << maxSamples << tcu::TestLog::EndMessage; // Don't even try to test high bits if there are none if ((m_flags & FLAGS_HIGH_BITS) && (m_samples % 32 == 0)) { m_testCtx.getLog() << tcu::TestLog::Message << "Sample count is multiple of word size. No unused high bits in sample mask.\nSkipping." << tcu::TestLog::EndMessage; throw tcu::NotSupportedError("Test requires unused high bits (sample count not multiple of 32)"); } // generate textures m_testCtx.getLog() << tcu::TestLog::Message << "Creating multisample texture with sample count " << m_samples << tcu::TestLog::EndMessage; gl.genTextures (1, &m_texID); gl.bindTexture (GL_TEXTURE_2D_MULTISAMPLE, m_texID); gl.texStorage2DMultisample (GL_TEXTURE_2D_MULTISAMPLE, m_samples, GL_RGBA8, m_canvasSize, m_canvasSize, GL_FALSE); GLU_EXPECT_NO_ERROR (gl.getError(), "texStorage2DMultisample"); // attach texture to fbo m_testCtx.getLog() << tcu::TestLog::Message << "Attaching texture to FBO" << tcu::TestLog::EndMessage; gl.genFramebuffers (1, &m_fboID); gl.bindFramebuffer (GL_FRAMEBUFFER, m_fboID); gl.framebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_texID, 0); GLU_EXPECT_NO_ERROR (gl.getError(), "framebufferTexture2D"); // buffers gl.genVertexArrays (1, &m_vaoID); GLU_EXPECT_NO_ERROR (gl.getError(), "genVertexArrays"); gl.genBuffers (1, &m_vboID); gl.bindBuffer (GL_ARRAY_BUFFER, m_vboID); GLU_EXPECT_NO_ERROR (gl.getError(), "genBuffers"); // generate grid pattern { std::vector<tcu::Vec4> gridData(m_gridsize*m_gridsize*6); for (int y = 0; y < m_gridsize; ++y) for (int x = 0; x < m_gridsize; ++x) { gridData[(y * m_gridsize + x)*6 + 0] = tcu::Vec4(((float)(x+0) / m_gridsize) * 2.0f - 1.0f, ((float)(y+0) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); gridData[(y * m_gridsize + x)*6 + 1] = tcu::Vec4(((float)(x+0) / m_gridsize) * 2.0f - 1.0f, ((float)(y+1) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); gridData[(y * m_gridsize + x)*6 + 2] = tcu::Vec4(((float)(x+1) / m_gridsize) * 2.0f - 1.0f, ((float)(y+1) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); gridData[(y * m_gridsize + x)*6 + 3] = tcu::Vec4(((float)(x+0) / m_gridsize) * 2.0f - 1.0f, ((float)(y+0) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); gridData[(y * m_gridsize + x)*6 + 4] = tcu::Vec4(((float)(x+1) / m_gridsize) * 2.0f - 1.0f, ((float)(y+1) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); gridData[(y * m_gridsize + x)*6 + 5] = tcu::Vec4(((float)(x+1) / m_gridsize) * 2.0f - 1.0f, ((float)(y+0) / m_gridsize) * 2.0f - 1.0f, 0.0f, 1.0f); } gl.bufferData (GL_ARRAY_BUFFER, (int)(gridData.size() * sizeof(tcu::Vec4)), gridData[0].getPtr(), GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR (gl.getError(), "bufferData"); } // generate programs genSamplerProgram(); genAlphaProgram(); } void SampleMaskCase::deinit (void) { if (m_texID) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texID); m_texID = 0; } if (m_vaoID) { m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vaoID); m_vaoID = 0; } if (m_vboID) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vboID); m_vboID = 0; } if (m_fboID) { m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_fboID); m_fboID = 0; } if (m_samplerProgram) { delete m_samplerProgram; m_samplerProgram = DE_NULL; } if (m_alphaProgram) { delete m_alphaProgram; m_alphaProgram = DE_NULL; } } SampleMaskCase::IterateResult SampleMaskCase::iterate (void) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", (m_currentSample == -1) ? ("Verifying with zero mask") : (std::string() + "Verifying sample " + de::toString(m_currentSample + 1) + "/" + de::toString(m_samples))); bool iterationOk; // Mask only one sample, clear rest updateTexture(m_currentSample); // Verify only one sample set is in the texture iterationOk = verifyTexture(m_currentSample); if (!iterationOk) m_allIterationsOk = false; m_currentSample++; if (m_currentSample < m_samples) return CONTINUE; // End result if (m_allIterationsOk) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else if (m_flags & FLAGS_HIGH_BITS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unused mask bits have effect"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Sample test failed"); return STOP; } void SampleMaskCase::genSamplerProgram (void) { const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; const char* const fragShaderSource = "#version 310 es\n" "layout(location = 0) out highp vec4 fragColor;\n" "uniform highp sampler2DMS u_sampler;\n" "uniform highp int u_sample;\n" "void main (void)\n" "{\n" " highp float correctCoverage = 0.0;\n" " highp float incorrectCoverage = 0.0;\n" " highp ivec2 texelPos = ivec2(int(floor(gl_FragCoord.x)), int(floor(gl_FragCoord.y)));\n" "\n" " for (int sampleNdx = 0; sampleNdx < ${NUMSAMPLES}; ++sampleNdx)\n" " {\n" " highp float sampleColor = texelFetch(u_sampler, texelPos, sampleNdx).r;\n" " if (sampleNdx == u_sample)\n" " correctCoverage += sampleColor;\n" " else\n" " incorrectCoverage += sampleColor;\n" " }\n" " fragColor = vec4(correctCoverage, incorrectCoverage, 0.0, 1.0);\n" "}\n"; const tcu::ScopedLogSection section (m_testCtx.getLog(), "GenerateSamplerShader", "Generate sampler shader"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); std::map<std::string, std::string> args; args["NUMSAMPLES"] = de::toString(m_samples); m_samplerProgram = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(tcu::StringTemplate(fragShaderSource).specialize(args))); m_testCtx.getLog() << *m_samplerProgram; if (!m_samplerProgram->isOk()) throw tcu::TestError("Could not create sampler program."); m_samplerProgramPosLoc = gl.getAttribLocation(m_samplerProgram->getProgram(), "a_position"); m_samplerProgramSamplerLoc = gl.getUniformLocation(m_samplerProgram->getProgram(), "u_sampler"); m_samplerProgramSampleNdxLoc = gl.getUniformLocation(m_samplerProgram->getProgram(), "u_sample"); } void SampleMaskCase::genAlphaProgram (void) { const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "out highp float v_alpha;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_alpha = (a_position.x * 0.5 + 0.5)*(a_position.y * 0.5 + 0.5);\n" "}\n"; const char* const fragShaderSource = "#version 310 es\n" "layout(location = 0) out highp vec4 fragColor;\n" "in mediump float v_alpha;\n" "void main (void)\n" "{\n" " fragColor = vec4(1.0, 1.0, 1.0, v_alpha);\n" "}\n"; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_alphaProgram = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(fragShaderSource)); if (!m_alphaProgram->isOk()) { m_testCtx.getLog() << *m_alphaProgram; throw tcu::TestError("Could not create aplha program."); } m_alphaProgramPosLoc = gl.getAttribLocation(m_alphaProgram->getProgram(), "a_position"); } void SampleMaskCase::updateTexture (int sample) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // prepare draw gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboID); gl.viewport(0, 0, m_canvasSize, m_canvasSize); gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); // clear all samples m_testCtx.getLog() << tcu::TestLog::Message << "Clearing image" << tcu::TestLog::EndMessage; gl.clear(GL_COLOR_BUFFER_BIT); // set mask state if (m_flags & FLAGS_HIGH_BITS) { const std::vector<deUint32> bitmask = genSetNthBitSampleMask(sample); const std::vector<deUint32> effectiveMask = genAllSetToNthBitSampleMask(m_samples); std::vector<deUint32> totalBitmask (effectiveMask.size()); DE_ASSERT((int)totalBitmask.size() == m_effectiveSampleMaskWordCount); // set some arbitrary high bits to non-effective bits for (int wordNdx = 0; wordNdx < (int)effectiveMask.size(); ++wordNdx) { const deUint32 randomMask = (deUint32)deUint32Hash(wordNdx << 2 ^ sample); const deUint32 sampleMask = (wordNdx < (int)bitmask.size()) ? (bitmask[wordNdx]) : (0); const deUint32 maskMask = effectiveMask[wordNdx]; totalBitmask[wordNdx] = (sampleMask & maskMask) | (randomMask & ~maskMask); } m_testCtx.getLog() << tcu::TestLog::Message << "Setting sample mask to 0b" << sampleMaskToString(totalBitmask, (int)totalBitmask.size() * 32) << tcu::TestLog::EndMessage; gl.enable(GL_SAMPLE_MASK); for (int wordNdx = 0; wordNdx < m_effectiveSampleMaskWordCount; ++wordNdx) { const GLbitfield wordmask = (wordNdx < (int)totalBitmask.size()) ? ((GLbitfield)(totalBitmask[wordNdx])) : (0); gl.sampleMaski((deUint32)wordNdx, wordmask); } } else { const std::vector<deUint32> bitmask = genSetNthBitSampleMask(sample); DE_ASSERT((int)bitmask.size() <= m_effectiveSampleMaskWordCount); m_testCtx.getLog() << tcu::TestLog::Message << "Setting sample mask to 0b" << sampleMaskToString(bitmask, m_samples) << tcu::TestLog::EndMessage; gl.enable(GL_SAMPLE_MASK); for (int wordNdx = 0; wordNdx < m_effectiveSampleMaskWordCount; ++wordNdx) { const GLbitfield wordmask = (wordNdx < (int)bitmask.size()) ? ((GLbitfield)(bitmask[wordNdx])) : (0); gl.sampleMaski((deUint32)wordNdx, wordmask); } } if (m_flags & FLAGS_ALPHA_TO_COVERAGE) { m_testCtx.getLog() << tcu::TestLog::Message << "Enabling alpha to coverage." << tcu::TestLog::EndMessage; gl.enable(GL_SAMPLE_ALPHA_TO_COVERAGE); } if (m_flags & FLAGS_SAMPLE_COVERAGE) { m_testCtx.getLog() << tcu::TestLog::Message << "Enabling sample coverage. Varying sample coverage for grid cells." << tcu::TestLog::EndMessage; gl.enable(GL_SAMPLE_COVERAGE); } // draw test pattern m_testCtx.getLog() << tcu::TestLog::Message << "Drawing test grid" << tcu::TestLog::EndMessage; gl.bindVertexArray (m_vaoID); gl.bindBuffer (GL_ARRAY_BUFFER, m_vboID); gl.vertexAttribPointer (m_alphaProgramPosLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray (m_alphaProgramPosLoc); GLU_EXPECT_NO_ERROR (gl.getError(), "vertexAttribPointer"); gl.useProgram (m_alphaProgram->getProgram()); for (int y = 0; y < m_gridsize; ++y) for (int x = 0; x < m_gridsize; ++x) { if (m_flags & FLAGS_SAMPLE_COVERAGE) gl.sampleCoverage((y*m_gridsize + x) / float(m_gridsize*m_gridsize), GL_FALSE); gl.drawArrays (GL_TRIANGLES, (y*m_gridsize + x) * 6, 6); GLU_EXPECT_NO_ERROR (gl.getError(), "drawArrays"); } // clean state gl.disableVertexAttribArray (m_alphaProgramPosLoc); gl.useProgram (0); gl.bindFramebuffer (GL_FRAMEBUFFER, 0); gl.disable (GL_SAMPLE_MASK); gl.disable (GL_SAMPLE_ALPHA_TO_COVERAGE); gl.disable (GL_SAMPLE_COVERAGE); GLU_EXPECT_NO_ERROR (gl.getError(), "clean"); } bool SampleMaskCase::verifyTexture (int sample) { tcu::Surface result (m_canvasSize, m_canvasSize); tcu::Surface errorMask (m_canvasSize, m_canvasSize); bool error = false; tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toVec()); // Draw sample: // Sample sampleNdx is set to red channel // Other samples are set to green channel drawSample(result, sample); // Check surface contains only sampleNdx for (int y = 0; y < m_canvasSize; ++y) for (int x = 0; x < m_canvasSize; ++x) { const tcu::RGBA color = result.getPixel(x, y); // Allow missing coverage with FLAGS_ALPHA_TO_COVERAGE and FLAGS_SAMPLE_COVERAGE, or if there are no samples enabled const bool allowMissingCoverage = ((m_flags & (FLAGS_ALPHA_TO_COVERAGE | FLAGS_SAMPLE_COVERAGE)) != 0) || (sample == -1); // disabled sample was written to if (color.getGreen() != 0) { error = true; errorMask.setPixel(x, y, tcu::RGBA::red); } // enabled sample was not written to else if (color.getRed() != 255 && !allowMissingCoverage) { error = true; errorMask.setPixel(x, y, tcu::RGBA::red); } } if (error) { m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed, disabled samples found." << tcu::TestLog::EndMessage << tcu::TestLog::ImageSet("VerificationResult", "Result of rendering") << tcu::TestLog::Image("Result", "Result", result) << tcu::TestLog::Image("ErrorMask", "Error Mask", errorMask) << tcu::TestLog::EndImageSet; return false; } else { m_testCtx.getLog() << tcu::TestLog::Message << "Image verification ok, no disabled samples found." << tcu::TestLog::EndMessage; return true; } } void SampleMaskCase::drawSample (tcu::Surface& dst, int sample) { // Downsample using only one sample static const tcu::Vec4 fullscreenQuad[] = { tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f) }; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glu::Buffer vertexBuffer (m_context.getRenderContext()); gl.bindTexture (GL_TEXTURE_2D_MULTISAMPLE, m_texID); gl.bindVertexArray (m_vaoID); gl.bindBuffer (GL_ARRAY_BUFFER, *vertexBuffer); gl.bufferData (GL_ARRAY_BUFFER, sizeof(fullscreenQuad), &fullscreenQuad[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR (gl.getError(), "bufferData"); gl.viewport (0, 0, m_canvasSize, m_canvasSize); gl.clearColor (0, 0, 0, 1); gl.clear (GL_COLOR_BUFFER_BIT); gl.vertexAttribPointer (m_samplerProgramPosLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray (m_samplerProgramPosLoc); GLU_EXPECT_NO_ERROR (gl.getError(), "vertexAttribPointer"); gl.useProgram (m_samplerProgram->getProgram()); gl.uniform1i (m_samplerProgramSamplerLoc, 0); gl.uniform1i (m_samplerProgramSampleNdxLoc, (deInt32)sample); GLU_EXPECT_NO_ERROR (gl.getError(), "useprogram"); m_testCtx.getLog() << tcu::TestLog::Message << "Reading from texture with sampler shader, u_sample = " << sample << tcu::TestLog::EndMessage; gl.drawArrays (GL_TRIANGLE_STRIP, 0, 4); GLU_EXPECT_NO_ERROR (gl.getError(), "drawArrays"); gl.disableVertexAttribArray (m_samplerProgramPosLoc); gl.useProgram (0); GLU_EXPECT_NO_ERROR (gl.getError(), "cleanup"); gl.finish (); glu::readPixels (m_context.getRenderContext(), 0, 0, dst.getAccess()); GLU_EXPECT_NO_ERROR (gl.getError(), "readPixels"); } class MultisampleTextureUsageCase : public TestCase { public: enum TextureType { TEXTURE_COLOR_2D = 0, TEXTURE_COLOR_2D_ARRAY, TEXTURE_INT_2D, TEXTURE_INT_2D_ARRAY, TEXTURE_UINT_2D, TEXTURE_UINT_2D_ARRAY, TEXTURE_DEPTH_2D, TEXTURE_DEPTH_2D_ARRAY, TEXTURE_LAST }; MultisampleTextureUsageCase (Context& ctx, const char* name, const char* desc, int samples, TextureType type); ~MultisampleTextureUsageCase (void); private: void init (void); void deinit (void); IterateResult iterate (void); void genDrawShader (void); void genSamplerShader (void); void renderToTexture (float value); void sampleTexture (tcu::Surface& dst, float value); bool verifyImage (const tcu::Surface& dst); static const int s_textureSize = 256; static const int s_textureArraySize = 8; static const int s_textureLayer = 3; const TextureType m_type; const int m_numSamples; glw::GLuint m_fboID; glw::GLuint m_textureID; glu::ShaderProgram* m_drawShader; glu::ShaderProgram* m_samplerShader; const bool m_isColorFormat; const bool m_isSignedFormat; const bool m_isUnsignedFormat; const bool m_isDepthFormat; const bool m_isArrayType; }; MultisampleTextureUsageCase::MultisampleTextureUsageCase (Context& ctx, const char* name, const char* desc, int samples, TextureType type) : TestCase (ctx, name, desc) , m_type (type) , m_numSamples (samples) , m_fboID (0) , m_textureID (0) , m_drawShader (DE_NULL) , m_samplerShader (DE_NULL) , m_isColorFormat (m_type == TEXTURE_COLOR_2D || m_type == TEXTURE_COLOR_2D_ARRAY) , m_isSignedFormat (m_type == TEXTURE_INT_2D || m_type == TEXTURE_INT_2D_ARRAY) , m_isUnsignedFormat(m_type == TEXTURE_UINT_2D || m_type == TEXTURE_UINT_2D_ARRAY) , m_isDepthFormat (m_type == TEXTURE_DEPTH_2D || m_type == TEXTURE_DEPTH_2D_ARRAY) , m_isArrayType (m_type == TEXTURE_COLOR_2D_ARRAY || m_type == TEXTURE_INT_2D_ARRAY || m_type == TEXTURE_UINT_2D_ARRAY || m_type == TEXTURE_DEPTH_2D_ARRAY) { DE_ASSERT(m_type < TEXTURE_LAST); } MultisampleTextureUsageCase::~MultisampleTextureUsageCase (void) { deinit(); } void MultisampleTextureUsageCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const glw::GLenum internalFormat = (m_isColorFormat) ? (GL_R8) : (m_isSignedFormat) ? (GL_R8I) : (m_isUnsignedFormat) ? (GL_R8UI) : (m_isDepthFormat) ? (GL_DEPTH_COMPONENT32F) : (0); const glw::GLenum textureTarget = (m_isArrayType) ? (GL_TEXTURE_2D_MULTISAMPLE_ARRAY) : (GL_TEXTURE_2D_MULTISAMPLE); const glw::GLenum fboAttachment = (m_isDepthFormat) ? (GL_DEPTH_ATTACHMENT) : (GL_COLOR_ATTACHMENT0); DE_ASSERT(internalFormat); // requirements if (m_isArrayType && !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array")) throw tcu::NotSupportedError("Test requires OES_texture_storage_multisample_2d_array extension"); if (m_context.getRenderTarget().getWidth() < s_textureSize || m_context.getRenderTarget().getHeight() < s_textureSize) throw tcu::NotSupportedError("render target size must be at least " + de::toString(static_cast<int>(s_textureSize)) + "x" + de::toString(static_cast<int>(s_textureSize))); { glw::GLint maxSamples = 0; gl.getInternalformativ(textureTarget, internalFormat, GL_SAMPLES, 1, &maxSamples); if (m_numSamples > maxSamples) throw tcu::NotSupportedError("Requested sample count is greater than supported"); m_testCtx.getLog() << tcu::TestLog::Message << "Max sample count for " << glu::getPixelFormatStr(internalFormat) << ": " << maxSamples << tcu::TestLog::EndMessage; } { GLint maxTextureSize = 0; gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); if (s_textureSize > maxTextureSize) throw tcu::NotSupportedError("Larger GL_MAX_TEXTURE_SIZE is required"); } if (m_isArrayType) { GLint maxTextureLayers = 0; gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureLayers); if (s_textureArraySize > maxTextureLayers) throw tcu::NotSupportedError("Larger GL_MAX_ARRAY_TEXTURE_LAYERS is required"); } // create texture m_testCtx.getLog() << tcu::TestLog::Message << "Creating multisample " << ((m_isDepthFormat) ? ("depth") : ("")) << " texture" << ((m_isArrayType) ? (" array") : ("")) << tcu::TestLog::EndMessage; gl.genTextures(1, &m_textureID); gl.bindTexture(textureTarget, m_textureID); GLU_EXPECT_NO_ERROR(gl.getError(), "bind texture"); if (m_isArrayType) gl.texStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, m_numSamples, internalFormat, s_textureSize, s_textureSize, s_textureArraySize, GL_FALSE); else gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_numSamples, internalFormat, s_textureSize, s_textureSize, GL_FALSE); GLU_EXPECT_NO_ERROR(gl.getError(), "texstorage"); // create fbo for drawing gl.genFramebuffers(1, &m_fboID); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboID); if (m_isArrayType) { m_testCtx.getLog() << tcu::TestLog::Message << "Attaching multisample texture array layer " << static_cast<int>(s_textureLayer) << " to fbo" << tcu::TestLog::EndMessage; gl.framebufferTextureLayer(GL_FRAMEBUFFER, fboAttachment, m_textureID, 0, s_textureLayer); } else { m_testCtx.getLog() << tcu::TestLog::Message << "Attaching multisample texture to fbo" << tcu::TestLog::EndMessage; gl.framebufferTexture2D(GL_FRAMEBUFFER, fboAttachment, textureTarget, m_textureID, 0); } GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo"); // create shader for rendering to fbo genDrawShader(); // create shader for sampling the texture rendered to genSamplerShader(); } void MultisampleTextureUsageCase::deinit (void) { if (m_textureID) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_textureID); m_textureID = 0; } if (m_fboID) { m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_fboID); m_fboID = 0; } if (m_drawShader) { delete m_drawShader; m_drawShader = DE_NULL; } if (m_samplerShader) { delete m_samplerShader; m_samplerShader = DE_NULL; } } MultisampleTextureUsageCase::IterateResult MultisampleTextureUsageCase::iterate (void) { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Sample", "Render to texture and sample texture"); tcu::Surface result (s_textureSize, s_textureSize); const float minValue = (m_isColorFormat || m_isDepthFormat) ? (0.0f) : (m_isSignedFormat) ? (-100.0f) : (m_isUnsignedFormat) ? (0.0f) : ( 1.0f); const float maxValue = (m_isColorFormat || m_isDepthFormat) ? (1.0f) : (m_isSignedFormat) ? ( 100.0f) : (m_isUnsignedFormat) ? (200.0f) : (-1.0f); de::Random rnd (deUint32Hash((deUint32)m_type)); const float rawValue = rnd.getFloat(minValue, maxValue); const float preparedValue = (m_isSignedFormat || m_isUnsignedFormat) ? (deFloatFloor(rawValue)) : (rawValue); // draw to fbo with a random value renderToTexture(preparedValue); // draw from texture to front buffer sampleTexture(result, preparedValue); // result is ok? if (verifyImage(result)) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); return STOP; } void MultisampleTextureUsageCase::genDrawShader (void) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "RenderShader", "Generate render-to-texture shader"); static const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; static const char* const fragmentShaderSourceColor = "#version 310 es\n" "layout(location = 0) out highp ${OUTTYPE} fragColor;\n" "uniform highp float u_writeValue;\n" "void main (void)\n" "{\n" " fragColor = ${OUTTYPE}(vec4(u_writeValue, 1.0, 1.0, 1.0));\n" "}\n"; static const char* const fragmentShaderSourceDepth = "#version 310 es\n" "layout(location = 0) out highp vec4 fragColor;\n" "uniform highp float u_writeValue;\n" "void main (void)\n" "{\n" " fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" " gl_FragDepth = u_writeValue;\n" "}\n"; const char* const fragmentSource = (m_isDepthFormat) ? (fragmentShaderSourceDepth) : (fragmentShaderSourceColor); std::map<std::string, std::string> fragmentArguments; if (m_isColorFormat || m_isDepthFormat) fragmentArguments["OUTTYPE"] = "vec4"; else if (m_isSignedFormat) fragmentArguments["OUTTYPE"] = "ivec4"; else if (m_isUnsignedFormat) fragmentArguments["OUTTYPE"] = "uvec4"; else DE_ASSERT(DE_FALSE); m_drawShader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(tcu::StringTemplate(fragmentSource).specialize(fragmentArguments))); m_testCtx.getLog() << *m_drawShader; if (!m_drawShader->isOk()) throw tcu::TestError("could not build shader"); } void MultisampleTextureUsageCase::genSamplerShader (void) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "SamplerShader", "Generate texture sampler shader"); static const char* const vertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "out highp float v_gradient;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_gradient = a_position.x * 0.5 + 0.5;\n" "}\n"; static const char* const fragmentShaderSource = "#version 310 es\n" "${EXTENSION_STATEMENT}" "layout(location = 0) out highp vec4 fragColor;\n" "uniform highp ${SAMPLERTYPE} u_sampler;\n" "uniform highp int u_maxSamples;\n" "uniform highp int u_layer;\n" "uniform highp float u_cmpValue;\n" "in highp float v_gradient;\n" "void main (void)\n" "{\n" " const highp vec4 okColor = vec4(0.0, 1.0, 0.0, 1.0);\n" " const highp vec4 failColor = vec4(1.0, 0.0, 0.0, 1.0);\n" " const highp float epsilon = ${EPSILON};\n" "\n" " highp int sampleNdx = clamp(int(floor(v_gradient * float(u_maxSamples))), 0, u_maxSamples-1);\n" " highp float value = float(texelFetch(u_sampler, ${FETCHPOS}, sampleNdx).r);\n" " fragColor = (abs(u_cmpValue - value) < epsilon) ? (okColor) : (failColor);\n" "}\n"; std::map<std::string, std::string> fragmentArguments; if (m_isArrayType) fragmentArguments["FETCHPOS"] = "ivec3(floor(gl_FragCoord.xy), u_layer)"; else fragmentArguments["FETCHPOS"] = "ivec2(floor(gl_FragCoord.xy))"; if (m_isColorFormat || m_isDepthFormat) fragmentArguments["EPSILON"] = "0.1"; else fragmentArguments["EPSILON"] = "1.0"; if (m_isArrayType) fragmentArguments["EXTENSION_STATEMENT"] = "#extension GL_OES_texture_storage_multisample_2d_array : require\n"; else fragmentArguments["EXTENSION_STATEMENT"] = ""; switch (m_type) { case TEXTURE_COLOR_2D: fragmentArguments["SAMPLERTYPE"] = "sampler2DMS"; break; case TEXTURE_COLOR_2D_ARRAY: fragmentArguments["SAMPLERTYPE"] = "sampler2DMSArray"; break; case TEXTURE_INT_2D: fragmentArguments["SAMPLERTYPE"] = "isampler2DMS"; break; case TEXTURE_INT_2D_ARRAY: fragmentArguments["SAMPLERTYPE"] = "isampler2DMSArray"; break; case TEXTURE_UINT_2D: fragmentArguments["SAMPLERTYPE"] = "usampler2DMS"; break; case TEXTURE_UINT_2D_ARRAY: fragmentArguments["SAMPLERTYPE"] = "usampler2DMSArray"; break; case TEXTURE_DEPTH_2D: fragmentArguments["SAMPLERTYPE"] = "sampler2DMS"; break; case TEXTURE_DEPTH_2D_ARRAY: fragmentArguments["SAMPLERTYPE"] = "sampler2DMSArray"; break; default: DE_ASSERT(DE_FALSE); } m_samplerShader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(tcu::StringTemplate(fragmentShaderSource).specialize(fragmentArguments))); m_testCtx.getLog() << *m_samplerShader; if (!m_samplerShader->isOk()) throw tcu::TestError("could not build shader"); } void MultisampleTextureUsageCase::renderToTexture (float value) { static const tcu::Vec4 fullscreenQuad[] = { tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), }; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int posLocation = gl.getAttribLocation(m_drawShader->getProgram(), "a_position"); const int valueLocation = gl.getUniformLocation(m_drawShader->getProgram(), "u_writeValue"); glu::Buffer vertexAttibBuffer (m_context.getRenderContext()); m_testCtx.getLog() << tcu::TestLog::Message << "Filling multisample texture with value " << value << tcu::TestLog::EndMessage; // upload data gl.bindBuffer(GL_ARRAY_BUFFER, *vertexAttibBuffer); gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad[0].getPtr(), GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "bufferdata"); // clear buffer gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboID); gl.viewport(0, 0, s_textureSize, s_textureSize); if (m_isColorFormat) { const float clearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; gl.clearBufferfv(GL_COLOR, 0, clearColor); } else if (m_isSignedFormat) { const deInt32 clearColor[4] = { 0, 0, 0, 0 }; gl.clearBufferiv(GL_COLOR, 0, clearColor); } else if (m_isUnsignedFormat) { const deUint32 clearColor[4] = { 0, 0, 0, 0 }; gl.clearBufferuiv(GL_COLOR, 0, clearColor); } else if (m_isDepthFormat) { const float clearDepth = 0.5f; gl.clearBufferfv(GL_DEPTH, 0, &clearDepth); } GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer"); // setup shader and draw gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray(posLocation); gl.useProgram(m_drawShader->getProgram()); gl.uniform1f(valueLocation, value); GLU_EXPECT_NO_ERROR(gl.getError(), "setup draw"); if (m_isDepthFormat) { gl.enable(GL_DEPTH_TEST); gl.depthFunc(GL_ALWAYS); } gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); // clean state if (m_isDepthFormat) gl.disable(GL_DEPTH_TEST); gl.disableVertexAttribArray(posLocation); gl.useProgram(0); gl.bindFramebuffer(GL_FRAMEBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "clean"); } void MultisampleTextureUsageCase::sampleTexture (tcu::Surface& dst, float value) { static const tcu::Vec4 fullscreenQuad[] = { tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), }; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int posLocation = gl.getAttribLocation(m_samplerShader->getProgram(), "a_position"); const int samplerLocation = gl.getUniformLocation(m_samplerShader->getProgram(), "u_sampler"); const int maxSamplesLocation = gl.getUniformLocation(m_samplerShader->getProgram(), "u_maxSamples"); const int layerLocation = gl.getUniformLocation(m_samplerShader->getProgram(), "u_layer"); const int valueLocation = gl.getUniformLocation(m_samplerShader->getProgram(), "u_cmpValue"); const glw::GLenum textureTarget = (m_isArrayType) ? (GL_TEXTURE_2D_MULTISAMPLE_ARRAY) : (GL_TEXTURE_2D_MULTISAMPLE); glu::Buffer vertexAttibBuffer (m_context.getRenderContext()); m_testCtx.getLog() << tcu::TestLog::Message << "Sampling from texture." << tcu::TestLog::EndMessage; // upload data gl.bindBuffer(GL_ARRAY_BUFFER, *vertexAttibBuffer); gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad[0].getPtr(), GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "bufferdata"); // clear gl.viewport(0, 0, s_textureSize, s_textureSize); gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.clear(GL_COLOR_BUFFER_BIT); // setup shader and draw gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray(posLocation); gl.useProgram(m_samplerShader->getProgram()); gl.uniform1i(samplerLocation, 0); gl.uniform1i(maxSamplesLocation, m_numSamples); if (m_isArrayType) gl.uniform1i(layerLocation, s_textureLayer); gl.uniform1f(valueLocation, value); gl.bindTexture(textureTarget, m_textureID); GLU_EXPECT_NO_ERROR(gl.getError(), "setup draw"); gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); // clean state gl.disableVertexAttribArray(posLocation); gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "clean"); // read results gl.finish(); glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); } bool MultisampleTextureUsageCase::verifyImage (const tcu::Surface& dst) { bool error = false; m_testCtx.getLog() << tcu::TestLog::Message << "Verifying image." << tcu::TestLog::EndMessage; for (int y = 0; y < dst.getHeight(); ++y) for (int x = 0; x < dst.getWidth(); ++x) { const tcu::RGBA color = dst.getPixel(x, y); const int colorThresholdRed = 1 << (8 - m_context.getRenderTarget().getPixelFormat().redBits); const int colorThresholdGreen = 1 << (8 - m_context.getRenderTarget().getPixelFormat().greenBits); const int colorThresholdBlue = 1 << (8 - m_context.getRenderTarget().getPixelFormat().blueBits); // only green is accepted if (color.getRed() > colorThresholdRed || color.getGreen() < 255 - colorThresholdGreen || color.getBlue() > colorThresholdBlue) error = true; } if (error) { m_testCtx.getLog() << tcu::TestLog::Message << "Invalid pixels found." << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::ImageSet("Verification result", "Result of rendering") << tcu::TestLog::Image("Result", "Result", dst) << tcu::TestLog::EndImageSet; return false; } else { m_testCtx.getLog() << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage; return true; } } class NegativeFramebufferCase : public TestCase { public: enum CaseType { CASE_DIFFERENT_N_SAMPLES_TEX = 0, CASE_DIFFERENT_N_SAMPLES_RBO, CASE_DIFFERENT_FIXED_TEX, CASE_DIFFERENT_FIXED_RBO, CASE_NON_ZERO_LEVEL, CASE_LAST }; NegativeFramebufferCase (Context& context, const char* name, const char* desc, CaseType caseType); ~NegativeFramebufferCase (void); private: void init (void); void deinit (void); IterateResult iterate (void); void getFormatSamples (glw::GLenum target, std::vector<int>& samples); const CaseType m_caseType; const int m_fboSize; const glw::GLenum m_internalFormat; int m_numSamples0; // !< samples for attachment 0 int m_numSamples1; // !< samples for attachment 1 }; NegativeFramebufferCase::NegativeFramebufferCase (Context& context, const char* name, const char* desc, CaseType caseType) : TestCase (context, name, desc) , m_caseType (caseType) , m_fboSize (64) , m_internalFormat (GL_RGBA8) , m_numSamples0 (-1) , m_numSamples1 (-1) { } NegativeFramebufferCase::~NegativeFramebufferCase (void) { deinit(); } void NegativeFramebufferCase::init (void) { const bool colorAttachmentTexture = (m_caseType == CASE_DIFFERENT_N_SAMPLES_TEX) || (m_caseType == CASE_DIFFERENT_FIXED_TEX); const bool colorAttachmentRbo = (m_caseType == CASE_DIFFERENT_N_SAMPLES_RBO) || (m_caseType == CASE_DIFFERENT_FIXED_RBO); const bool useDifferentSampleCounts= (m_caseType == CASE_DIFFERENT_N_SAMPLES_TEX) || (m_caseType == CASE_DIFFERENT_N_SAMPLES_RBO); std::vector<int> textureSamples; std::vector<int> rboSamples; getFormatSamples(GL_TEXTURE_2D_MULTISAMPLE, textureSamples); getFormatSamples(GL_RENDERBUFFER, rboSamples); TCU_CHECK(!textureSamples.empty()); TCU_CHECK(!rboSamples.empty()); // select sample counts if (useDifferentSampleCounts) { if (colorAttachmentTexture) { m_numSamples0 = textureSamples[0]; if (textureSamples.size() >= 2) m_numSamples1 = textureSamples[1]; else throw tcu::NotSupportedError("Test requires multiple supported sample counts"); } else if (colorAttachmentRbo) { for (int texNdx = 0; texNdx < (int)textureSamples.size(); ++texNdx) for (int rboNdx = 0; rboNdx < (int)rboSamples.size(); ++rboNdx) { if (textureSamples[texNdx] != rboSamples[rboNdx]) { m_numSamples0 = textureSamples[texNdx]; m_numSamples1 = rboSamples[rboNdx]; return; } } throw tcu::NotSupportedError("Test requires multiple supported sample counts"); } else DE_ASSERT(DE_FALSE); } else { if (colorAttachmentTexture) { m_numSamples0 = textureSamples[0]; m_numSamples1 = textureSamples[0]; } else if (colorAttachmentRbo) { for (int texNdx = 0; texNdx < (int)textureSamples.size(); ++texNdx) for (int rboNdx = 0; rboNdx < (int)rboSamples.size(); ++rboNdx) { if (textureSamples[texNdx] == rboSamples[rboNdx]) { m_numSamples0 = textureSamples[texNdx]; m_numSamples1 = rboSamples[rboNdx]; return; } } throw tcu::NotSupportedError("Test requires a sample count supported in both rbo and texture"); } else { m_numSamples0 = textureSamples[0]; } } } void NegativeFramebufferCase::deinit (void) { } NegativeFramebufferCase::IterateResult NegativeFramebufferCase::iterate (void) { const bool colorAttachmentTexture = (m_caseType == CASE_DIFFERENT_N_SAMPLES_TEX) || (m_caseType == CASE_DIFFERENT_FIXED_TEX); const bool colorAttachmentRbo = (m_caseType == CASE_DIFFERENT_N_SAMPLES_RBO) || (m_caseType == CASE_DIFFERENT_FIXED_RBO); const glw::GLenum fixedSampleLocations0 = (m_caseType == CASE_DIFFERENT_N_SAMPLES_RBO) ? (GL_TRUE) : (GL_FALSE); const glw::GLenum fixedSampleLocations1 = ((m_caseType == CASE_DIFFERENT_FIXED_TEX) || (m_caseType == CASE_DIFFERENT_FIXED_RBO)) ? (GL_TRUE) : (GL_FALSE); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); glw::GLuint fboId = 0; glw::GLuint rboId = 0; glw::GLuint tex0Id = 0; glw::GLuint tex1Id = 0; bool testFailed = false; gl.enableLogging(true); try { gl.glGenFramebuffers(1, &fboId); gl.glBindFramebuffer(GL_FRAMEBUFFER, fboId); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen fbo"); gl.glGenTextures(1, &tex0Id); gl.glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex0Id); gl.glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_numSamples0, m_internalFormat, m_fboSize, m_fboSize, fixedSampleLocations0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen texture 0"); if (m_caseType == CASE_NON_ZERO_LEVEL) { glw::GLenum error; // attaching non-zero level generates invalid value gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex0Id, 5); error = gl.glGetError(); if (error != GL_INVALID_VALUE) { m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Expected GL_INVALID_VALUE, got " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; testFailed = true; } } else { gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex0Id, 0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "attach to c0"); if (colorAttachmentTexture) { gl.glGenTextures(1, &tex1Id); gl.glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex1Id); gl.glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_numSamples1, m_internalFormat, m_fboSize, m_fboSize, fixedSampleLocations1); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen texture 1"); gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, tex1Id, 0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "attach to c1"); } else if (colorAttachmentRbo) { gl.glGenRenderbuffers(1, &rboId); gl.glBindRenderbuffer(GL_RENDERBUFFER, rboId); gl.glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples1, m_internalFormat, m_fboSize, m_fboSize); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen rb"); gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, rboId); GLU_EXPECT_NO_ERROR(gl.glGetError(), "attach to c1"); } else DE_ASSERT(DE_FALSE); // should not be complete { glw::GLenum status = gl.glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) { m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Expected GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, got " << glu::getFramebufferStatusName(status) << tcu::TestLog::EndMessage; testFailed = true; } } } } catch (...) { gl.glDeleteFramebuffers(1, &fboId); gl.glDeleteRenderbuffers(1, &rboId); gl.glDeleteTextures(1, &tex0Id); gl.glDeleteTextures(1, &tex1Id); throw; } gl.glDeleteFramebuffers(1, &fboId); gl.glDeleteRenderbuffers(1, &rboId); gl.glDeleteTextures(1, &tex0Id); gl.glDeleteTextures(1, &tex1Id); if (testFailed) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong error code"); else m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } void NegativeFramebufferCase::getFormatSamples (glw::GLenum target, std::vector<int>& samples) { const glw::Functions gl = m_context.getRenderContext().getFunctions(); int sampleCount = 0; gl.getInternalformativ(target, m_internalFormat, GL_NUM_SAMPLE_COUNTS, 1, &sampleCount); samples.resize(sampleCount); if (sampleCount > 0) { gl.getInternalformativ(target, m_internalFormat, GL_SAMPLES, sampleCount, &samples[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "get max samples"); } } class NegativeTexParameterCase : public TestCase { public: enum TexParam { TEXTURE_MIN_FILTER = 0, TEXTURE_MAG_FILTER, TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_WRAP_R, TEXTURE_MIN_LOD, TEXTURE_MAX_LOD, TEXTURE_COMPARE_MODE, TEXTURE_COMPARE_FUNC, TEXTURE_BASE_LEVEL, TEXTURE_LAST }; NegativeTexParameterCase (Context& context, const char* name, const char* desc, TexParam param); ~NegativeTexParameterCase (void); private: void init (void); void deinit (void); IterateResult iterate (void); glw::GLenum getParamGLEnum (void) const; glw::GLint getParamValue (void) const; glw::GLenum getExpectedError (void) const; const TexParam m_texParam; int m_iteration; }; NegativeTexParameterCase::NegativeTexParameterCase (Context& context, const char* name, const char* desc, TexParam param) : TestCase (context, name, desc) , m_texParam (param) , m_iteration (0) { DE_ASSERT(param < TEXTURE_LAST); } NegativeTexParameterCase::~NegativeTexParameterCase (void) { deinit(); } void NegativeTexParameterCase::init (void) { // default value m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } void NegativeTexParameterCase::deinit (void) { } NegativeTexParameterCase::IterateResult NegativeTexParameterCase::iterate (void) { static const struct TextureType { const char* name; glw::GLenum target; glw::GLenum internalFormat; bool isArrayType; } types[] = { { "color", GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, false }, { "color array", GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_RGBA8, true }, { "signed integer", GL_TEXTURE_2D_MULTISAMPLE, GL_R8I, false }, { "signed integer array", GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_R8I, true }, { "unsigned integer", GL_TEXTURE_2D_MULTISAMPLE, GL_R8UI, false }, { "unsigned integer array", GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_R8UI, true }, }; const tcu::ScopedLogSection scope(m_testCtx.getLog(), "Iteration", std::string() + "Testing parameter with " + types[m_iteration].name + " texture"); if (types[m_iteration].isArrayType && !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array")) m_testCtx.getLog() << tcu::TestLog::Message << "GL_OES_texture_storage_multisample_2d_array not supported, skipping target" << tcu::TestLog::EndMessage; else { glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); glu::Texture texture (m_context.getRenderContext()); glw::GLenum error; gl.enableLogging(true); // gen texture gl.glBindTexture(types[m_iteration].target, *texture); if (types[m_iteration].isArrayType) gl.glTexStorage3DMultisample(types[m_iteration].target, 1, types[m_iteration].internalFormat, 16, 16, 16, GL_FALSE); else gl.glTexStorage2DMultisample(types[m_iteration].target, 1, types[m_iteration].internalFormat, 16, 16, GL_FALSE); GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup texture"); // set param gl.glTexParameteri(types[m_iteration].target, getParamGLEnum(), getParamValue()); error = gl.glGetError(); // expect failure if (error != getExpectedError()) { m_testCtx.getLog() << tcu::TestLog::Message << "Got unexpected error: " << glu::getErrorStr(error) << ", expected: " << glu::getErrorStr(getExpectedError()) << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong error code"); } } if (++m_iteration < DE_LENGTH_OF_ARRAY(types)) return CONTINUE; return STOP; } glw::GLenum NegativeTexParameterCase::getParamGLEnum (void) const { switch (m_texParam) { case TEXTURE_MIN_FILTER: return GL_TEXTURE_MIN_FILTER; case TEXTURE_MAG_FILTER: return GL_TEXTURE_MAG_FILTER; case TEXTURE_WRAP_S: return GL_TEXTURE_WRAP_S; case TEXTURE_WRAP_T: return GL_TEXTURE_WRAP_T; case TEXTURE_WRAP_R: return GL_TEXTURE_WRAP_R; case TEXTURE_MIN_LOD: return GL_TEXTURE_MIN_LOD; case TEXTURE_MAX_LOD: return GL_TEXTURE_MAX_LOD; case TEXTURE_COMPARE_MODE: return GL_TEXTURE_COMPARE_MODE; case TEXTURE_COMPARE_FUNC: return GL_TEXTURE_COMPARE_FUNC; case TEXTURE_BASE_LEVEL: return GL_TEXTURE_BASE_LEVEL; default: DE_ASSERT(DE_FALSE); return 0; } } glw::GLint NegativeTexParameterCase::getParamValue (void) const { switch (m_texParam) { case TEXTURE_MIN_FILTER: return GL_LINEAR; case TEXTURE_MAG_FILTER: return GL_LINEAR; case TEXTURE_WRAP_S: return GL_CLAMP_TO_EDGE; case TEXTURE_WRAP_T: return GL_CLAMP_TO_EDGE; case TEXTURE_WRAP_R: return GL_CLAMP_TO_EDGE; case TEXTURE_MIN_LOD: return 1; case TEXTURE_MAX_LOD: return 5; case TEXTURE_COMPARE_MODE: return GL_NONE; case TEXTURE_COMPARE_FUNC: return GL_NOTEQUAL; case TEXTURE_BASE_LEVEL: return 2; default: DE_ASSERT(DE_FALSE); return 0; } } glw::GLenum NegativeTexParameterCase::getExpectedError (void) const { switch (m_texParam) { case TEXTURE_MIN_FILTER: return GL_INVALID_ENUM; case TEXTURE_MAG_FILTER: return GL_INVALID_ENUM; case TEXTURE_WRAP_S: return GL_INVALID_ENUM; case TEXTURE_WRAP_T: return GL_INVALID_ENUM; case TEXTURE_WRAP_R: return GL_INVALID_ENUM; case TEXTURE_MIN_LOD: return GL_INVALID_ENUM; case TEXTURE_MAX_LOD: return GL_INVALID_ENUM; case TEXTURE_COMPARE_MODE: return GL_INVALID_ENUM; case TEXTURE_COMPARE_FUNC: return GL_INVALID_ENUM; case TEXTURE_BASE_LEVEL: return GL_INVALID_OPERATION; default: DE_ASSERT(DE_FALSE); return 0; } } class NegativeTexureSampleCase : public TestCase { public: enum SampleCountParam { SAMPLECOUNT_HIGH = 0, SAMPLECOUNT_ZERO, SAMPLECOUNT_LAST }; NegativeTexureSampleCase (Context& context, const char* name, const char* desc, SampleCountParam param); private: IterateResult iterate (void); const SampleCountParam m_sampleParam; }; NegativeTexureSampleCase::NegativeTexureSampleCase (Context& context, const char* name, const char* desc, SampleCountParam param) : TestCase (context, name, desc) , m_sampleParam (param) { DE_ASSERT(param < SAMPLECOUNT_LAST); } NegativeTexureSampleCase::IterateResult NegativeTexureSampleCase::iterate (void) { const glw::GLenum expectedError = (m_sampleParam == SAMPLECOUNT_HIGH) ? (GL_INVALID_OPERATION) : (GL_INVALID_VALUE); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); glu::Texture texture (m_context.getRenderContext()); glw::GLenum error; int samples = -1; gl.enableLogging(true); // calc samples if (m_sampleParam == SAMPLECOUNT_HIGH) { int maxSamples = 0; gl.glGetInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_SAMPLES, 1, &maxSamples); GLU_EXPECT_NO_ERROR(gl.glGetError(), "glGetInternalformativ"); samples = maxSamples + 1; } else if (m_sampleParam == SAMPLECOUNT_ZERO) samples = 0; else DE_ASSERT(DE_FALSE); // create texture with bad values gl.glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, *texture); gl.glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA8, 64, 64, GL_FALSE); error = gl.glGetError(); // expect failure if (error == expectedError) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else { m_testCtx.getLog() << tcu::TestLog::Message << "Got unexpected error: " << glu::getErrorStr(error) << ", expected: " << glu::getErrorStr(expectedError) << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong error code"); } return STOP; } } // anonymous TextureMultisampleTests::TextureMultisampleTests (Context& context) : TestCaseGroup(context, "multisample", "Multisample texture tests") { } TextureMultisampleTests::~TextureMultisampleTests (void) { } void TextureMultisampleTests::init (void) { static const int sampleCounts[] = { 1, 2, 3, 4, 8, 10, 12, 13, 16, 64 }; static const struct TextureType { const char* name; MultisampleTextureUsageCase::TextureType type; } textureTypes[] = { { "texture_color_2d", MultisampleTextureUsageCase::TEXTURE_COLOR_2D }, { "texture_color_2d_array", MultisampleTextureUsageCase::TEXTURE_COLOR_2D_ARRAY }, { "texture_int_2d", MultisampleTextureUsageCase::TEXTURE_INT_2D }, { "texture_int_2d_array", MultisampleTextureUsageCase::TEXTURE_INT_2D_ARRAY }, { "texture_uint_2d", MultisampleTextureUsageCase::TEXTURE_UINT_2D }, { "texture_uint_2d_array", MultisampleTextureUsageCase::TEXTURE_UINT_2D_ARRAY }, { "texture_depth_2d", MultisampleTextureUsageCase::TEXTURE_DEPTH_2D }, { "texture_depth_2d_array", MultisampleTextureUsageCase::TEXTURE_DEPTH_2D_ARRAY }, }; // .samples_x for (int sampleNdx = 0; sampleNdx < DE_LENGTH_OF_ARRAY(sampleCounts); ++sampleNdx) { tcu::TestCaseGroup* const sampleGroup = new tcu::TestCaseGroup(m_testCtx, (std::string("samples_") + de::toString(sampleCounts[sampleNdx])).c_str(), "Test with N samples"); addChild(sampleGroup); // position query works sampleGroup->addChild(new SamplePosRasterizationTest(m_context, "sample_position", "test SAMPLE_POSITION", sampleCounts[sampleNdx])); // sample mask is ANDed properly sampleGroup->addChild(new SampleMaskCase(m_context, "sample_mask_only", "Test with SampleMask only", sampleCounts[sampleNdx], SampleMaskCase::FLAGS_NONE)); sampleGroup->addChild(new SampleMaskCase(m_context, "sample_mask_and_alpha_to_coverage", "Test with SampleMask and alpha to coverage", sampleCounts[sampleNdx], SampleMaskCase::FLAGS_ALPHA_TO_COVERAGE)); sampleGroup->addChild(new SampleMaskCase(m_context, "sample_mask_and_sample_coverage", "Test with SampleMask and sample coverage", sampleCounts[sampleNdx], SampleMaskCase::FLAGS_SAMPLE_COVERAGE)); sampleGroup->addChild(new SampleMaskCase(m_context, "sample_mask_and_sample_coverage_and_alpha_to_coverage", "Test with SampleMask, sample coverage, and alpha to coverage", sampleCounts[sampleNdx], SampleMaskCase::FLAGS_ALPHA_TO_COVERAGE | SampleMaskCase::FLAGS_SAMPLE_COVERAGE)); // high bits cause no unexpected behavior sampleGroup->addChild(new SampleMaskCase(m_context, "sample_mask_non_effective_bits", "Test with SampleMask, set higher bits than sample count", sampleCounts[sampleNdx], SampleMaskCase::FLAGS_HIGH_BITS)); // usage for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(textureTypes); ++typeNdx) sampleGroup->addChild(new MultisampleTextureUsageCase(m_context, (std::string("use_") + textureTypes[typeNdx].name).c_str(), textureTypes[typeNdx].name, sampleCounts[sampleNdx], textureTypes[typeNdx].type)); } // .negative { tcu::TestCaseGroup* const negativeGroup = new tcu::TestCaseGroup(m_testCtx, "negative", "Negative tests"); addChild(negativeGroup); negativeGroup->addChild(new NegativeFramebufferCase (m_context, "fbo_attach_different_sample_count_tex_tex", "Attach different sample counts", NegativeFramebufferCase::CASE_DIFFERENT_N_SAMPLES_TEX)); negativeGroup->addChild(new NegativeFramebufferCase (m_context, "fbo_attach_different_sample_count_tex_rbo", "Attach different sample counts", NegativeFramebufferCase::CASE_DIFFERENT_N_SAMPLES_RBO)); negativeGroup->addChild(new NegativeFramebufferCase (m_context, "fbo_attach_different_fixed_state_tex_tex", "Attach fixed and non fixed", NegativeFramebufferCase::CASE_DIFFERENT_FIXED_TEX)); negativeGroup->addChild(new NegativeFramebufferCase (m_context, "fbo_attach_different_fixed_state_tex_rbo", "Attach fixed and non fixed", NegativeFramebufferCase::CASE_DIFFERENT_FIXED_RBO)); negativeGroup->addChild(new NegativeFramebufferCase (m_context, "fbo_attach_non_zero_level", "Attach non-zero level", NegativeFramebufferCase::CASE_NON_ZERO_LEVEL)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_min_filter", "set TEXTURE_MIN_FILTER", NegativeTexParameterCase::TEXTURE_MIN_FILTER)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_mag_filter", "set TEXTURE_MAG_FILTER", NegativeTexParameterCase::TEXTURE_MAG_FILTER)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_wrap_s", "set TEXTURE_WRAP_S", NegativeTexParameterCase::TEXTURE_WRAP_S)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_wrap_t", "set TEXTURE_WRAP_T", NegativeTexParameterCase::TEXTURE_WRAP_T)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_wrap_r", "set TEXTURE_WRAP_R", NegativeTexParameterCase::TEXTURE_WRAP_R)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_min_lod", "set TEXTURE_MIN_LOD", NegativeTexParameterCase::TEXTURE_MIN_LOD)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_max_lod", "set TEXTURE_MAX_LOD", NegativeTexParameterCase::TEXTURE_MAX_LOD)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_compare_mode", "set TEXTURE_COMPARE_MODE", NegativeTexParameterCase::TEXTURE_COMPARE_MODE)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_compare_func", "set TEXTURE_COMPARE_FUNC", NegativeTexParameterCase::TEXTURE_COMPARE_FUNC)); negativeGroup->addChild(new NegativeTexParameterCase(m_context, "texture_base_level", "set TEXTURE_BASE_LEVEL", NegativeTexParameterCase::TEXTURE_BASE_LEVEL)); negativeGroup->addChild(new NegativeTexureSampleCase(m_context, "texture_high_sample_count", "TexStorage with high numSamples", NegativeTexureSampleCase::SAMPLECOUNT_HIGH)); negativeGroup->addChild(new NegativeTexureSampleCase(m_context, "texture_zero_sample_count", "TexStorage with zero numSamples", NegativeTexureSampleCase::SAMPLECOUNT_ZERO)); } } } // Functional } // gles31 } // deqp