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
MultiviewCase(Context & context,const char * name,const char * description,int numSamples)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
~MultiviewCase()103 MultiviewCase::~MultiviewCase()
104 {
105 MultiviewCase::deinit();
106 }
107
setupFramebufferObjects()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
deleteFramebufferObjects()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
init()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
deinit()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
iterate()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 if (x > backbufferWidth / 2)
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
MultiviewTests(Context & context)344 MultiviewTests::MultiviewTests(Context& context) : TestCaseGroup(context, "multiview", "Multiview Tests")
345 {
346 }
347
~MultiviewTests()348 MultiviewTests::~MultiviewTests()
349 {
350 }
351
init()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