1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.0 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2018 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ /*! 20 * \file 21 * \brief Multiview tests. 22 * Tests functionality provided by the three multiview extensions. 23 * Note that this file is formatted using external/openglcts/.clang-format 24 */ /*--------------------------------------------------------------------*/ 25 26 #include "es3fMultiviewTests.hpp" 27 28 #include "deString.h" 29 #include "deStringUtil.hpp" 30 #include "gluContextInfo.hpp" 31 #include "gluPixelTransfer.hpp" 32 #include "gluShaderProgram.hpp" 33 #include "glw.h" 34 #include "glwEnums.hpp" 35 #include "glwFunctions.hpp" 36 #include "tcuRenderTarget.hpp" 37 #include "tcuSurface.hpp" 38 #include "tcuTestLog.hpp" 39 #include "tcuVector.hpp" 40 41 using tcu::TestLog; 42 using tcu::Vec4; 43 44 namespace deqp 45 { 46 namespace gles3 47 { 48 namespace Functional 49 { 50 51 static const int NUM_CASE_ITERATIONS = 1; 52 static const float UNIT_SQUARE[16] = { 53 1.0f, 1.0f, 0.05f, 1.0f, // Vertex 0 54 1.0f, -1.0f, 0.05f, 1.0f, // Vertex 1 55 -1.0f, 1.0f, 0.05f, 1.0f, // Vertex 2 56 -1.0f, -1.0f, 0.05f, 1.0f // Vertex 3 57 }; 58 static const float COLOR_VALUES[] = { 59 1, 0, 0, 1, // Red for level 0 60 0, 1, 0, 1, // Green for level 1 61 }; 62 63 class MultiviewCase : public TestCase 64 { 65 public: 66 MultiviewCase(Context& context, const char* name, const char* description, int numSamples); 67 ~MultiviewCase(); 68 void init(); 69 void deinit(); 70 IterateResult iterate(); 71 72 private: 73 MultiviewCase(const MultiviewCase& other); 74 MultiviewCase& operator=(const MultiviewCase& other); 75 void setupFramebufferObjects(); 76 void deleteFramebufferObjects(); 77 78 glu::ShaderProgram* m_multiviewProgram; 79 deUint32 m_multiviewFbo; 80 deUint32 m_arrayTexture; 81 82 glu::ShaderProgram* m_finalProgram; 83 84 int m_caseIndex; 85 const int m_numSamples; 86 const int m_width; 87 const int m_height; 88 }; 89 90 MultiviewCase::MultiviewCase(Context& context, const char* name, const char* description, int numSamples) 91 : TestCase(context, name, description) 92 , m_multiviewProgram(DE_NULL) 93 , m_multiviewFbo(0) 94 , m_arrayTexture(0) 95 , m_finalProgram(DE_NULL) 96 , m_caseIndex(0) 97 , m_numSamples(numSamples) 98 , m_width(512) 99 , m_height(512) 100 { 101 } 102 103 MultiviewCase::~MultiviewCase() 104 { 105 MultiviewCase::deinit(); 106 } 107 108 void MultiviewCase::setupFramebufferObjects() 109 { 110 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 111 112 // First create the array texture and multiview FBO. 113 114 gl.genTextures(1, &m_arrayTexture); 115 gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture); 116 gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1 /* num mipmaps */, GL_RGBA8, m_width / 2, m_height, 2 /* num levels */); 117 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 118 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 119 GLU_EXPECT_NO_ERROR(gl.getError(), "Create array texture"); 120 121 gl.genFramebuffers(1, &m_multiviewFbo); 122 gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo); 123 if (m_numSamples == 1) 124 { 125 gl.framebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture, 0 /* mip level */, 126 0 /* base view index */, 2 /* num views */); 127 } 128 else 129 { 130 gl.framebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture, 131 0 /* mip level */, m_numSamples /* samples */, 132 0 /* base view index */, 2 /* num views */); 133 } 134 GLU_EXPECT_NO_ERROR(gl.getError(), "Create multiview FBO"); 135 deUint32 fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); 136 if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED) 137 { 138 throw tcu::NotSupportedError("Framebuffer unsupported", "", __FILE__, __LINE__); 139 } 140 else if (fboStatus != GL_FRAMEBUFFER_COMPLETE) 141 { 142 throw tcu::TestError("Failed to create framebuffer object", "", __FILE__, __LINE__); 143 } 144 } 145 146 void MultiviewCase::deleteFramebufferObjects() 147 { 148 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 149 gl.deleteTextures(1, &m_arrayTexture); 150 gl.deleteFramebuffers(1, &m_multiviewFbo); 151 } 152 153 void MultiviewCase::init() 154 { 155 const glu::ContextInfo& contextInfo = m_context.getContextInfo(); 156 bool mvsupported = contextInfo.isExtensionSupported("GL_OVR_multiview"); 157 if (!mvsupported) 158 { 159 TCU_THROW(NotSupportedError, "Multiview is not supported"); 160 } 161 162 if (m_numSamples > 1) 163 { 164 bool msaasupported = contextInfo.isExtensionSupported("GL_OVR_multiview_multisampled_render_to_texture"); 165 if (!msaasupported) 166 { 167 TCU_THROW(NotSupportedError, "Implicit MSAA multiview is not supported"); 168 } 169 } 170 171 const char* multiviewVertexShader = "#version 300 es\n" 172 "#extension GL_OVR_multiview : enable\n" 173 "layout(num_views=2) in;\n" 174 "layout(location = 0) in mediump vec4 a_position;\n" 175 "uniform mediump vec4 uColor[2];\n" 176 "out mediump vec4 vColor;\n" 177 "void main() {\n" 178 " vColor = uColor[gl_ViewID_OVR];\n" 179 " gl_Position = a_position;\n" 180 "}\n"; 181 182 const char* multiviewFragmentShader = "#version 300 es\n" 183 "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" 184 "in mediump vec4 vColor;\n" 185 "void main() {\n" 186 " dEQP_FragColor = vColor;\n" 187 "}\n"; 188 189 m_multiviewProgram = new glu::ShaderProgram( 190 m_context.getRenderContext(), glu::makeVtxFragSources(multiviewVertexShader, multiviewFragmentShader)); 191 DE_ASSERT(m_multiviewProgram); 192 if (!m_multiviewProgram->isOk()) 193 { 194 m_testCtx.getLog() << *m_multiviewProgram; 195 TCU_FAIL("Failed to compile multiview shader"); 196 } 197 198 // Draw the first layer on the left half of the screen and the second layer 199 // on the right half. 200 const char* finalVertexShader = "#version 300 es\n" 201 "layout(location = 0) in mediump vec4 a_position;\n" 202 "out highp vec3 vTexCoord;\n" 203 "void main() {\n" 204 " vTexCoord.x = fract(a_position.x + 1.0);\n" 205 " vTexCoord.y = .5 * (a_position.y + 1.0);\n" 206 " vTexCoord.z = a_position.x;\n" 207 " gl_Position = a_position;\n" 208 "}\n"; 209 210 const char* finalFragmentShader = "#version 300 es\n" 211 "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" 212 "uniform lowp sampler2DArray uArrayTexture;\n" 213 "in highp vec3 vTexCoord;\n" 214 "void main() {\n" 215 " highp vec3 uvw = vTexCoord;\n" 216 " uvw.z = floor(vTexCoord.z + 1.0);\n" 217 " dEQP_FragColor = texture(uArrayTexture, uvw);\n" 218 "}\n"; 219 220 m_finalProgram = new glu::ShaderProgram(m_context.getRenderContext(), 221 glu::makeVtxFragSources(finalVertexShader, finalFragmentShader)); 222 DE_ASSERT(m_finalProgram); 223 if (!m_finalProgram->isOk()) 224 { 225 m_testCtx.getLog() << *m_finalProgram; 226 TCU_FAIL("Failed to compile final shader"); 227 } 228 229 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 230 GLU_CHECK_MSG("Case initialization finished"); 231 } 232 233 void MultiviewCase::deinit() 234 { 235 deleteFramebufferObjects(); 236 delete m_multiviewProgram; 237 m_multiviewProgram = DE_NULL; 238 delete m_finalProgram; 239 m_finalProgram = DE_NULL; 240 } 241 242 MultiviewCase::IterateResult MultiviewCase::iterate() 243 { 244 TestLog& log = m_testCtx.getLog(); 245 deUint32 colorUniform = glGetUniformLocation(m_multiviewProgram->getProgram(), "uColor"); 246 std::string header = "Case iteration " + de::toString(m_caseIndex + 1) + " / " + de::toString(NUM_CASE_ITERATIONS); 247 log << TestLog::Section(header, header); 248 249 DE_ASSERT(m_multiviewProgram); 250 251 // Create and bind the multiview FBO. 252 253 try 254 { 255 setupFramebufferObjects(); 256 } 257 catch (tcu::NotSupportedError& e) 258 { 259 log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection; 260 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported"); 261 return STOP; 262 } 263 catch (tcu::InternalError& e) 264 { 265 log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection; 266 m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); 267 return STOP; 268 } 269 270 log << TestLog::EndSection; 271 272 // Draw full screen quad into the multiview framebuffer. 273 // The quad should be instanced into both layers of the array texture. 274 275 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 276 gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo); 277 gl.viewport(0, 0, m_width / 2, m_height); 278 gl.useProgram(m_multiviewProgram->getProgram()); 279 gl.uniform4fv(colorUniform, 2, COLOR_VALUES); 280 gl.enableVertexAttribArray(0); 281 gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &UNIT_SQUARE[0]); 282 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); 283 284 // Sample from the array texture to draw a quad into the backbuffer. 285 286 const int backbufferWidth = m_context.getRenderTarget().getWidth(); 287 const int backbufferHeight = m_context.getRenderTarget().getHeight(); 288 gl.bindFramebuffer(GL_FRAMEBUFFER, 0); 289 gl.viewport(0, 0, backbufferWidth, backbufferHeight); 290 gl.useProgram(m_finalProgram->getProgram()); 291 gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture); 292 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); 293 294 // Read back the framebuffer, ensure that the left half is red and the 295 // right half is green. 296 297 tcu::Surface pixels(backbufferWidth, backbufferHeight); 298 glu::readPixels(m_context.getRenderContext(), 0, 0, pixels.getAccess()); 299 bool failed = false; 300 for (int y = 0; y < backbufferHeight; y++) 301 { 302 for (int x = 0; x < backbufferWidth; x++) 303 { 304 tcu::RGBA pixel = pixels.getPixel(x, y); 305 if (x < backbufferWidth / 2) 306 { 307 if (pixel.getRed() != 255 || pixel.getGreen() != 0 || pixel.getBlue() != 0) 308 { 309 failed = true; 310 } 311 } 312 else 313 { 314 if (pixel.getRed() != 0 || pixel.getGreen() != 255 || pixel.getBlue() != 0) 315 { 316 failed = true; 317 } 318 } 319 if (failed) 320 { 321 break; 322 } 323 } 324 } 325 326 deleteFramebufferObjects(); 327 328 if (failed) 329 { 330 log << TestLog::Image("Result image", "Result image", pixels); 331 } 332 333 log << TestLog::Message << "Test result: " << (failed ? "Failed!" : "Passed!") << TestLog::EndMessage; 334 335 if (failed) 336 { 337 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 338 return STOP; 339 } 340 341 return (++m_caseIndex < NUM_CASE_ITERATIONS) ? CONTINUE : STOP; 342 } 343 344 MultiviewTests::MultiviewTests(Context& context) : TestCaseGroup(context, "multiview", "Multiview Tests") 345 { 346 } 347 348 MultiviewTests::~MultiviewTests() 349 { 350 } 351 352 void MultiviewTests::init() 353 { 354 addChild(new MultiviewCase(m_context, "samples_1", "Multiview test without multisampling", 1)); 355 addChild(new MultiviewCase(m_context, "samples_2", "Multiview test with MSAAx2", 2)); 356 addChild(new MultiviewCase(m_context, "samples_4", "Multiview test without MSAAx4", 4)); 357 } 358 359 } // namespace Functional 360 } // namespace gles3 361 } // namespace deqp 362