/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2018 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 Multiview tests. * Tests functionality provided by the three multiview extensions. * Note that this file is formatted using external/openglcts/.clang-format */ /*--------------------------------------------------------------------*/ #include "es3fMultiviewTests.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "gluContextInfo.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "glw.h" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuRenderTarget.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "tcuVector.hpp" using tcu::TestLog; using tcu::Vec4; namespace deqp { namespace gles3 { namespace Functional { static const int NUM_CASE_ITERATIONS = 1; static const float UNIT_SQUARE[16] = { 1.0f, 1.0f, 0.05f, 1.0f, // Vertex 0 1.0f, -1.0f, 0.05f, 1.0f, // Vertex 1 -1.0f, 1.0f, 0.05f, 1.0f, // Vertex 2 -1.0f, -1.0f, 0.05f, 1.0f // Vertex 3 }; static const float COLOR_VALUES[] = { 1, 0, 0, 1, // Red for level 0 0, 1, 0, 1, // Green for level 1 }; class MultiviewCase : public TestCase { public: MultiviewCase(Context& context, const char* name, const char* description, int numSamples); ~MultiviewCase(); void init(); void deinit(); IterateResult iterate(); private: MultiviewCase(const MultiviewCase& other); MultiviewCase& operator=(const MultiviewCase& other); void setupFramebufferObjects(); void deleteFramebufferObjects(); glu::ShaderProgram* m_multiviewProgram; deUint32 m_multiviewFbo; deUint32 m_arrayTexture; glu::ShaderProgram* m_finalProgram; int m_caseIndex; const int m_numSamples; const int m_width; const int m_height; }; MultiviewCase::MultiviewCase(Context& context, const char* name, const char* description, int numSamples) : TestCase(context, name, description) , m_multiviewProgram(DE_NULL) , m_multiviewFbo(0) , m_arrayTexture(0) , m_finalProgram(DE_NULL) , m_caseIndex(0) , m_numSamples(numSamples) , m_width(512) , m_height(512) { } MultiviewCase::~MultiviewCase() { MultiviewCase::deinit(); } void MultiviewCase::setupFramebufferObjects() { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // First create the array texture and multiview FBO. gl.genTextures(1, &m_arrayTexture); gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture); gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1 /* num mipmaps */, GL_RGBA8, m_width / 2, m_height, 2 /* num levels */); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GLU_EXPECT_NO_ERROR(gl.getError(), "Create array texture"); gl.genFramebuffers(1, &m_multiviewFbo); gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo); if (m_numSamples == 1) { gl.framebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture, 0 /* mip level */, 0 /* base view index */, 2 /* num views */); } else { gl.framebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture, 0 /* mip level */, m_numSamples /* samples */, 0 /* base view index */, 2 /* num views */); } GLU_EXPECT_NO_ERROR(gl.getError(), "Create multiview FBO"); deUint32 fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED) { throw tcu::NotSupportedError("Framebuffer unsupported", "", __FILE__, __LINE__); } else if (fboStatus != GL_FRAMEBUFFER_COMPLETE) { throw tcu::TestError("Failed to create framebuffer object", "", __FILE__, __LINE__); } } void MultiviewCase::deleteFramebufferObjects() { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.deleteTextures(1, &m_arrayTexture); gl.deleteFramebuffers(1, &m_multiviewFbo); } void MultiviewCase::init() { const glu::ContextInfo& contextInfo = m_context.getContextInfo(); bool mvsupported = contextInfo.isExtensionSupported("GL_OVR_multiview"); if (!mvsupported) { TCU_THROW(NotSupportedError, "Multiview is not supported"); } if (m_numSamples > 1) { bool msaasupported = contextInfo.isExtensionSupported("GL_OVR_multiview_multisampled_render_to_texture"); if (!msaasupported) { TCU_THROW(NotSupportedError, "Implicit MSAA multiview is not supported"); } } const char* multiviewVertexShader = "#version 300 es\n" "#extension GL_OVR_multiview : enable\n" "layout(num_views=2) in;\n" "layout(location = 0) in mediump vec4 a_position;\n" "uniform mediump vec4 uColor[2];\n" "out mediump vec4 vColor;\n" "void main() {\n" " vColor = uColor[gl_ViewID_OVR];\n" " gl_Position = a_position;\n" "}\n"; const char* multiviewFragmentShader = "#version 300 es\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "in mediump vec4 vColor;\n" "void main() {\n" " dEQP_FragColor = vColor;\n" "}\n"; m_multiviewProgram = new glu::ShaderProgram( m_context.getRenderContext(), glu::makeVtxFragSources(multiviewVertexShader, multiviewFragmentShader)); DE_ASSERT(m_multiviewProgram); if (!m_multiviewProgram->isOk()) { m_testCtx.getLog() << *m_multiviewProgram; TCU_FAIL("Failed to compile multiview shader"); } // Draw the first layer on the left half of the screen and the second layer // on the right half. const char* finalVertexShader = "#version 300 es\n" "layout(location = 0) in mediump vec4 a_position;\n" "out highp vec3 vTexCoord;\n" "void main() {\n" " vTexCoord.x = fract(a_position.x + 1.0);\n" " vTexCoord.y = .5 * (a_position.y + 1.0);\n" " vTexCoord.z = a_position.x;\n" " gl_Position = a_position;\n" "}\n"; const char* finalFragmentShader = "#version 300 es\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "uniform lowp sampler2DArray uArrayTexture;\n" "in highp vec3 vTexCoord;\n" "void main() {\n" " highp vec3 uvw = vTexCoord;\n" " uvw.z = floor(vTexCoord.z + 1.0);\n" " dEQP_FragColor = texture(uArrayTexture, uvw);\n" "}\n"; m_finalProgram = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(finalVertexShader, finalFragmentShader)); DE_ASSERT(m_finalProgram); if (!m_finalProgram->isOk()) { m_testCtx.getLog() << *m_finalProgram; TCU_FAIL("Failed to compile final shader"); } m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); GLU_CHECK_MSG("Case initialization finished"); } void MultiviewCase::deinit() { deleteFramebufferObjects(); delete m_multiviewProgram; m_multiviewProgram = DE_NULL; delete m_finalProgram; m_finalProgram = DE_NULL; } MultiviewCase::IterateResult MultiviewCase::iterate() { TestLog& log = m_testCtx.getLog(); deUint32 colorUniform = glGetUniformLocation(m_multiviewProgram->getProgram(), "uColor"); std::string header = "Case iteration " + de::toString(m_caseIndex + 1) + " / " + de::toString(NUM_CASE_ITERATIONS); log << TestLog::Section(header, header); DE_ASSERT(m_multiviewProgram); // Create and bind the multiview FBO. try { setupFramebufferObjects(); } catch (tcu::NotSupportedError& e) { log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection; m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported"); return STOP; } catch (tcu::InternalError& e) { log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); return STOP; } log << TestLog::EndSection; // Draw full screen quad into the multiview framebuffer. // The quad should be instanced into both layers of the array texture. const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo); gl.viewport(0, 0, m_width / 2, m_height); gl.useProgram(m_multiviewProgram->getProgram()); gl.uniform4fv(colorUniform, 2, COLOR_VALUES); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &UNIT_SQUARE[0]); gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); // Sample from the array texture to draw a quad into the backbuffer. const int backbufferWidth = m_context.getRenderTarget().getWidth(); const int backbufferHeight = m_context.getRenderTarget().getHeight(); gl.bindFramebuffer(GL_FRAMEBUFFER, 0); gl.viewport(0, 0, backbufferWidth, backbufferHeight); gl.useProgram(m_finalProgram->getProgram()); gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture); gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); // Read back the framebuffer, ensure that the left half is red and the // right half is green. tcu::Surface pixels(backbufferWidth, backbufferHeight); glu::readPixels(m_context.getRenderContext(), 0, 0, pixels.getAccess()); bool failed = false; for (int y = 0; y < backbufferHeight; y++) { for (int x = 0; x < backbufferWidth; x++) { tcu::RGBA pixel = pixels.getPixel(x, y); if (x < backbufferWidth / 2) { if (pixel.getRed() != 255 || pixel.getGreen() != 0 || pixel.getBlue() != 0) { failed = true; } } else if (x > backbufferWidth / 2) { if (pixel.getRed() != 0 || pixel.getGreen() != 255 || pixel.getBlue() != 0) { failed = true; } } if (failed) { break; } } } deleteFramebufferObjects(); if (failed) { log << TestLog::Image("Result image", "Result image", pixels); } log << TestLog::Message << "Test result: " << (failed ? "Failed!" : "Passed!") << TestLog::EndMessage; if (failed) { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return STOP; } return (++m_caseIndex < NUM_CASE_ITERATIONS) ? CONTINUE : STOP; } MultiviewTests::MultiviewTests(Context& context) : TestCaseGroup(context, "multiview", "Multiview Tests") { } MultiviewTests::~MultiviewTests() { } void MultiviewTests::init() { addChild(new MultiviewCase(m_context, "samples_1", "Multiview test without multisampling", 1)); addChild(new MultiviewCase(m_context, "samples_2", "Multiview test with MSAAx2", 2)); addChild(new MultiviewCase(m_context, "samples_4", "Multiview test without MSAAx4", 4)); } } // namespace Functional } // namespace gles3 } // namespace deqp