/*------------------------------------------------------------------------- * 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 Framebuffer without attachments (GL_ARB_framebuffer_no_attachments) tests. *//*--------------------------------------------------------------------*/ #include "es31fFboNoAttachmentTests.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "gluRenderContext.hpp" #include "gluDefs.hpp" #include "gluShaderProgram.hpp" #include "tcuTestContext.hpp" #include "tcuVectorType.hpp" #include "tcuVectorUtil.hpp" #include "tcuTestLog.hpp" #include "tcuCommandLine.hpp" #include "tcuResultCollector.hpp" #include "deMemory.h" #include "deRandom.hpp" #include "deString.h" #include "deStringUtil.hpp" #include <string> #include <vector> namespace deqp { namespace gles31 { namespace Functional { namespace { using namespace glw; using tcu::IVec2; using tcu::TestLog; using std::stringstream; using std::string; using std::vector; bool checkFramebufferSize (TestLog& log, const glu::RenderContext& renderCtx, GLuint framebuffer, const IVec2& size) { const glw::Functions& gl = renderCtx.getFunctions(); const char* const vertexSource = "#version 310 es\n" "in layout(location = 0) highp vec2 a_position;\n\n" "void main()\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; const char* const fragmentSource = "#version 310 es\n" "uniform layout(location = 0) highp ivec2 u_expectedSize;\n" "out layout(location = 0) mediump vec4 f_color;\n\n" "void main()\n" "{\n" " if (ivec2(gl_FragCoord.xy) != u_expectedSize) discard;\n" " f_color = vec4(1.0, 0.5, 0.25, 1.0);\n" "}\n"; const glu::ShaderProgram program (renderCtx, glu::makeVtxFragSources(vertexSource, fragmentSource)); GLuint query = 0; GLuint insidePassed = 0; GLuint outsideXPassed = 0; GLuint outsideYPassed = 0; if (!program.isOk()) log << program; TCU_CHECK(program.isOk()); gl.useProgram(program.getProgram()); gl.enable(GL_DEPTH_TEST); gl.depthFunc(GL_ALWAYS); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); gl.viewport(0, 0, size.x()*2, size.y()*2); // Oversized viewport so that it will not accidentally limit us to the correct size log << TestLog::Message << "Using " << size.x()*2 << "x" << size.y()*2 << " viewport" << TestLog::EndMessage; log << TestLog::Message << "Discarding fragments outside pixel of interest" << TestLog::EndMessage; log << TestLog::Message << "Using occlusion query to check for rendered fragments" << TestLog::EndMessage; TCU_CHECK(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); // Render { const float data[] = { 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, }; GLuint vertexArray = 0; GLuint vertexBuffer = 0; gl.genQueries(1, &query); gl.genVertexArrays(1, &vertexArray); gl.bindVertexArray(vertexArray); gl.genBuffers(1, &vertexBuffer); gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuffer); gl.bufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, GL_FLOAT, false, 0, DE_NULL); gl.uniform2i(0, size.x()-1, size.y()-1); gl.beginQuery(GL_ANY_SAMPLES_PASSED, query); gl.drawArrays(GL_TRIANGLES, 0, 6); gl.endQuery(GL_ANY_SAMPLES_PASSED); gl.getQueryObjectuiv(query, GL_QUERY_RESULT, &insidePassed); log << TestLog::Message << "A fragment was not discarded at (" << size.x()-1 << ", " << size.y()-1 << "). " << "Occlusion query reports it was " << (insidePassed > 0 ? "rendered." : "not rendered") << TestLog::EndMessage; gl.uniform2i(0, size.x(), size.y()-1); gl.beginQuery(GL_ANY_SAMPLES_PASSED, query); gl.drawArrays(GL_TRIANGLES, 0, 6); gl.endQuery(GL_ANY_SAMPLES_PASSED); gl.getQueryObjectuiv(query, GL_QUERY_RESULT, &outsideXPassed); log << TestLog::Message << "A fragment was not discarded at (" << size.x() << ", " << size.y()-1 << "). " << "Occlusion query reports it was " << (outsideXPassed > 0 ? "rendered." : "not rendered") << TestLog::EndMessage; gl.uniform2i(0, size.x()-1, size.y()); gl.beginQuery(GL_ANY_SAMPLES_PASSED, query); gl.drawArrays(GL_TRIANGLES, 0, 6); gl.endQuery(GL_ANY_SAMPLES_PASSED); gl.getQueryObjectuiv(query, GL_QUERY_RESULT, &outsideYPassed); log << TestLog::Message << "A fragment was not discarded at (" << size.x()-1 << ", " << size.y() << "). " << "Occlusion query reports it was " << (outsideYPassed > 0 ? "rendered." : "not rendered") << TestLog::EndMessage; gl.disableVertexAttribArray(0); gl.bindBuffer(GL_ARRAY_BUFFER, 0); gl.bindVertexArray(0); gl.deleteBuffers(1, &vertexBuffer); gl.deleteVertexArrays(1, &vertexArray); } gl.deleteQueries(1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed"); return insidePassed && !outsideXPassed && !outsideYPassed; } bool checkFramebufferRenderable (TestLog& log, const glu::RenderContext& renderCtx, GLuint framebuffer, const IVec2& size) { const glw::Functions& gl = renderCtx.getFunctions(); const char* const vertexSource = "#version 310 es\n" "in layout(location = 0) highp vec2 a_position;\n\n" "void main()\n" "{\n" " gl_Position = vec4(a_position, 0.0, 1.0);\n" "}\n"; const char* const fragmentSource = "#version 310 es\n" "out layout(location = 0) mediump vec4 f_color;\n\n" "void main()\n" "{\n" " f_color = vec4(1.0, 0.5, 0.25, 1.0);\n" "}\n"; const glu::ShaderProgram program (renderCtx, glu::makeVtxFragSources(vertexSource, fragmentSource)); GLuint query = 0; if (!program.isOk()) log << program; TCU_CHECK(program.isOk()); gl.useProgram(program.getProgram()); gl.enable(GL_DEPTH_TEST); gl.depthFunc(GL_ALWAYS); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); gl.viewport(0, 0, size.x(), size.y()); TCU_CHECK(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); log << TestLog::Message << "Rendering full framebuffer quad with color ouput, verifying output presence with occlusion query" << TestLog::EndMessage; // Render { const float data[] = { 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, }; GLuint vertexArray = 0; GLuint vertexBuffer = 0; gl.genQueries(1, &query); gl.genVertexArrays(1, &vertexArray); gl.bindVertexArray(vertexArray); gl.genBuffers(1, &vertexBuffer); gl.bindBuffer(GL_ARRAY_BUFFER, vertexBuffer); gl.bufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, GL_FLOAT, false, 0, DE_NULL); gl.beginQuery(GL_ANY_SAMPLES_PASSED, query); gl.drawArrays(GL_TRIANGLES, 0, 6); gl.endQuery(GL_ANY_SAMPLES_PASSED); gl.disableVertexAttribArray(0); gl.bindBuffer(GL_ARRAY_BUFFER, 0); gl.bindVertexArray(0); gl.deleteBuffers(1, &vertexBuffer); gl.deleteVertexArrays(1, &vertexArray); } // Read { GLuint passed = 0; gl.getQueryObjectuiv(query, GL_QUERY_RESULT, &passed); gl.deleteQueries(1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed"); if (passed) log << TestLog::Message << "Query passed" << TestLog::EndMessage; else log << TestLog::Message << "Query did not pass" << TestLog::EndMessage; return passed != 0; } } class FramebufferCompletenessCase : public tcu::TestCase { public: FramebufferCompletenessCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc); virtual ~FramebufferCompletenessCase (void) {} virtual IterateResult iterate (void); private: const glu::RenderContext& m_renderCtx; tcu::ResultCollector m_results; }; FramebufferCompletenessCase::FramebufferCompletenessCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc) : TestCase (testCtx, name, desc) , m_renderCtx (renderCtx) { } FramebufferCompletenessCase::IterateResult FramebufferCompletenessCase::iterate (void) { const glw::Functions& gl = m_renderCtx.getFunctions(); GLuint framebuffer = 0; gl.genFramebuffers(1, &framebuffer); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); m_results.check(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, "Framebuffer was incorrectly reported as complete when it had no width, height or attachments"); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 16); m_results.check(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, "Framebuffer was incorrectly reported as complete when it only had a width"); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 16); m_results.check(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer not reported as complete when it had width and height set"); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 0); m_results.check(gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, "Framebuffer was incorrectly reported as complete when it only had a height"); gl.deleteFramebuffers(1, &framebuffer); m_results.setTestContextResult(m_testCtx); return STOP; } struct FboSpec { int width; int height; int samples; FboSpec(int width_, int height_, int samples_) : width(width_), height(height_), samples(samples_){} }; class SizeCase : public tcu::TestCase { public: SizeCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc, const FboSpec& spec); virtual ~SizeCase (void) {} virtual IterateResult iterate (void); enum { USE_MAXIMUM = -1 }; private: int getWidth (void) const; int getHeight (void) const; int getSamples (void) const; const glu::RenderContext& m_renderCtx; const FboSpec m_spec; }; SizeCase::SizeCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc, const FboSpec& spec) : TestCase (testCtx, name, desc) , m_renderCtx (renderCtx) , m_spec (spec) { } SizeCase::IterateResult SizeCase::iterate (void) { const glw::Functions& gl = m_renderCtx.getFunctions(); TestLog& log = m_testCtx.getLog(); GLuint framebuffer = 0; const int width = getWidth(); const int height = getHeight(); const int samples = getSamples(); gl.genFramebuffers(1, &framebuffer); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, width); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, height); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES, samples); log << TestLog::Message << "Verifying " << width << "x" << height << " framebuffer with " << samples << "x multisampling" << TestLog::EndMessage; if(checkFramebufferRenderable(log, m_renderCtx, framebuffer, IVec2(width, height)) && checkFramebufferSize(log, m_renderCtx, framebuffer, IVec2(width, height))) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Framebuffer did not behave as expected"); gl.deleteFramebuffers(1, &framebuffer); return STOP; } int SizeCase::getWidth (void) const { if (m_spec.width != USE_MAXIMUM) return m_spec.width; else { const glw::Functions& gl = m_renderCtx.getFunctions(); GLint width = 0; gl.getIntegerv(GL_MAX_FRAMEBUFFER_WIDTH, &width); return width; } } int SizeCase::getHeight (void) const { if (m_spec.height != USE_MAXIMUM) return m_spec.height; else { const glw::Functions& gl = m_renderCtx.getFunctions(); GLint height = 0; gl.getIntegerv(GL_MAX_FRAMEBUFFER_HEIGHT, &height); return height; } } int SizeCase::getSamples (void) const { if (m_spec.samples != USE_MAXIMUM) return m_spec.samples; else { const glw::Functions& gl = m_renderCtx.getFunctions(); GLint samples = 0; gl.getIntegerv(GL_MAX_FRAMEBUFFER_SAMPLES, &samples); return samples; } } class AttachmentInteractionCase : public tcu::TestCase { public: AttachmentInteractionCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc, const FboSpec& defaultSpec, const FboSpec& attachmentSpec); virtual ~AttachmentInteractionCase (void) {} virtual IterateResult iterate (void); private: const glu::RenderContext& m_renderCtx; const FboSpec m_defaultSpec; const FboSpec m_attachmentSpec; }; AttachmentInteractionCase::AttachmentInteractionCase (tcu::TestContext& testCtx, const glu::RenderContext& renderCtx, const char* name, const char* desc, const FboSpec& defaultSpec, const FboSpec& attachmentSpec) : TestCase (testCtx, name, desc) , m_renderCtx (renderCtx) , m_defaultSpec (defaultSpec) , m_attachmentSpec (attachmentSpec) { } AttachmentInteractionCase::IterateResult AttachmentInteractionCase::iterate (void) { const glw::Functions& gl = m_renderCtx.getFunctions(); TestLog& log = m_testCtx.getLog(); GLuint framebuffer = 0; GLuint renderbuffer= 0; gl.genFramebuffers(1, &framebuffer); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, m_defaultSpec.width); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, m_defaultSpec.height); gl.framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES, m_defaultSpec.samples); gl.genRenderbuffers(1, &renderbuffer); gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer); gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_attachmentSpec.samples, GL_RGBA8, m_attachmentSpec.width, m_attachmentSpec.height); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); log << TestLog::Message << "Verifying " << m_attachmentSpec.width << "x" << m_attachmentSpec.height << " framebuffer with " << m_attachmentSpec.samples << "x multisampling" << " and defaults set to " << m_defaultSpec.width << "x" << m_defaultSpec.height << " with " << m_defaultSpec.samples << "x multisampling" << TestLog::EndMessage; if(checkFramebufferRenderable(log, m_renderCtx, framebuffer, IVec2(m_attachmentSpec.width, m_attachmentSpec.height)) && checkFramebufferSize(log, m_renderCtx, framebuffer, IVec2(m_attachmentSpec.width, m_attachmentSpec.height))) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Framebuffer did not behave as expected"); gl.deleteRenderbuffers(1, &renderbuffer); gl.deleteFramebuffers(1, &framebuffer); return STOP; } } // Anonymous tcu::TestCaseGroup* createFboNoAttachmentTests(Context& context) { const glu::RenderContext& renderCtx = context.getRenderContext(); tcu::TestContext& testCtx = context.getTestContext(); const int maxWidth = 2048; // MAX_FRAMEBUFFER_WIDTH in ES 3.1 const int maxHeight = 2048; // MAX_FRAMEBUFFER_HEIGHT in ES 3.1 const int maxSamples = 4; tcu::TestCaseGroup* const root = new tcu::TestCaseGroup(testCtx, "no_attachments", "Framebuffer without attachments"); // Size { tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "size", "Basic functionality tests with varying default size"); root->addChild(group); for (int width = 16; width <= maxWidth; width *= 4) { for (int height = 16; height <= maxHeight; height *= 4) { const FboSpec spec (width, height, 0); stringstream name; name << width << "x" << height; group->addChild(new SizeCase(testCtx, renderCtx, name.str().c_str(), name.str().c_str(), spec)); } } } // NPOT size { const FboSpec specs[] = { // Square FboSpec(1, 1, 0), FboSpec(3, 3, 0), FboSpec(15, 15, 0), FboSpec(17, 17, 0), FboSpec(31, 31, 0), FboSpec(33, 33, 0), FboSpec(63, 63, 0), FboSpec(65, 65, 0), FboSpec(127, 127, 0), FboSpec(129, 129, 0), FboSpec(255, 255, 0), FboSpec(257, 257, 0), FboSpec(511, 511, 0), FboSpec(513, 513, 0), FboSpec(1023, 1023, 0), FboSpec(1025, 1025, 0), FboSpec(2047, 2047, 0), // Non-square FboSpec(15, 511, 0), FboSpec(127, 15, 0), FboSpec(129, 127, 0), FboSpec(511, 127, 0), FboSpec(2047, 1025, 0), }; tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "npot_size", "Basic functionality with Non-power-of-two size"); root->addChild(group); for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(specs); caseNdx++) { const FboSpec& spec = specs[caseNdx]; stringstream name; name << spec.width << "x" << spec.height; group->addChild(new SizeCase(testCtx, renderCtx, name.str().c_str(), name.str().c_str(), spec)); } } // Multisample { tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "multisample", "Basic functionality with multisampled fbo"); root->addChild(group); for (int samples = 0; samples <= maxSamples; samples++) { const FboSpec spec (128, 128, samples); stringstream name; name << "samples" << samples; group->addChild(new SizeCase(testCtx, renderCtx, name.str().c_str(), name.str().c_str(), spec)); } } // Randomized { tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "random", "Randomized size & multisampling"); de::Random rng (0xF0E1E2D3 ^ testCtx.getCommandLine().getBaseSeed()); root->addChild(group); for (int caseNdx = 0; caseNdx < 16; caseNdx++) { const int width = rng.getInt(1, maxWidth); const int height = rng.getInt(1, maxHeight); const int samples = rng.getInt(0, maxSamples); const FboSpec spec (width, height, samples); const string name = de::toString(caseNdx); group->addChild(new SizeCase(testCtx, renderCtx, name.c_str(), name.c_str(), spec)); } } // Normal fbo with defaults set { tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "interaction", "Interaction of default parameters with normal fbo"); root->addChild(group); const FboSpec specs[][2] = { { FboSpec(256, 256, 0), FboSpec(128, 128, 1) }, { FboSpec(256, 256, 1), FboSpec(128, 128, 0) }, { FboSpec(256, 256, 0), FboSpec(512, 512, 2) }, { FboSpec(256, 256, 2), FboSpec(128, 512, 0) }, { FboSpec(127, 127, 0), FboSpec(129, 129, 0) }, { FboSpec(17, 512, 4), FboSpec(16, 16, 2) }, { FboSpec(2048, 2048, 4), FboSpec(1, 1, 0) }, { FboSpec(1, 1, 0), FboSpec(2048, 2048, 4) }, }; for (int specNdx = 0; specNdx < DE_LENGTH_OF_ARRAY(specs); specNdx++) { const FboSpec& baseSpec = specs[specNdx][0]; const FboSpec& altSpec = specs[specNdx][1]; stringstream baseSpecName, altSpecName; baseSpecName << baseSpec.width << "x" << baseSpec.height << "ms" << baseSpec.samples; altSpecName << altSpec.width << "x" << altSpec.height << "ms" << altSpec.samples; { const string name = baseSpecName.str() + "_default_" + altSpecName.str(); group->addChild(new AttachmentInteractionCase(testCtx, renderCtx, name.c_str(), name.c_str(), altSpec, baseSpec)); } } } // Maximums { tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, "maximums", "Maximum dimensions"); root->addChild(group); group->addChild(new SizeCase(testCtx, renderCtx, "width", "Maximum width", FboSpec(SizeCase::USE_MAXIMUM, 128, 0))); group->addChild(new SizeCase(testCtx, renderCtx, "height", "Maximum height", FboSpec(128, SizeCase::USE_MAXIMUM, 0))); group->addChild(new SizeCase(testCtx, renderCtx, "size", "Maximum size", FboSpec(SizeCase::USE_MAXIMUM, SizeCase::USE_MAXIMUM, 0))); group->addChild(new SizeCase(testCtx, renderCtx, "samples", "Maximum samples", FboSpec(128, 128, SizeCase::USE_MAXIMUM))); group->addChild(new SizeCase(testCtx, renderCtx, "all", "Maximum size & samples", FboSpec(SizeCase::USE_MAXIMUM, SizeCase::USE_MAXIMUM, SizeCase::USE_MAXIMUM))); } return root; } tcu::TestCaseGroup* createFboNoAttachmentCompletenessTests(Context& context) { TestCaseGroup* const group = new TestCaseGroup(context, "completeness", "Completeness tests"); group->addChild(new FramebufferCompletenessCase(context.getTestContext(), context.getRenderContext(), "no_attachments", "No attachments completeness")); return group; } } // Functional } // gles31 } // deqp