blob: 88d77add16179fe19732e6591626ce3313b4c062 [file] [log] [blame]
Martin Radev14a26ae2017-07-24 15:56:29 +03001//
2// Copyright 2017 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// Multiview draw tests:
7// Test issuing multiview Draw* commands.
8//
9
10#include "test_utils/ANGLETest.h"
11#include "test_utils/gl_raii.h"
12
13using namespace angle;
14
15class MultiviewDrawTest : public ANGLETest
16{
17 protected:
18 MultiviewDrawTest()
19 {
20 setWindowWidth(128);
21 setWindowHeight(128);
22 setWebGLCompatibilityEnabled(true);
23 }
Martin Radev7cf61662017-07-26 17:10:53 +030024 virtual ~MultiviewDrawTest() {}
Martin Radev14a26ae2017-07-24 15:56:29 +030025
26 void SetUp() override
27 {
28 ANGLETest::SetUp();
29
30 glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>(
31 eglGetProcAddress("glRequestExtensionANGLE"));
32 }
33
34 // Requests the ANGLE_multiview extension and returns true if the operation succeeds.
35 bool requestMultiviewExtension()
36 {
37 if (extensionRequestable("GL_ANGLE_multiview"))
38 {
39 glRequestExtensionANGLE("GL_ANGLE_multiview");
40 }
41
42 if (!extensionEnabled("GL_ANGLE_multiview"))
43 {
44 std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl;
45 return false;
46 }
47 return true;
48 }
49
50 PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
51};
52
Martin Radev7cf61662017-07-26 17:10:53 +030053class MultiviewDrawValidationTest : public MultiviewDrawTest
54{
55 protected:
56 MultiviewDrawValidationTest() {}
57
58 void SetUp() override
59 {
60 MultiviewDrawTest::SetUp();
61
62 glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
63
64 glBindTexture(GL_TEXTURE_2D, mTex2d);
65 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
66
67 glBindVertexArray(mVao);
68
69 const float kVertexData[3] = {0.0f};
70 glBindBuffer(GL_ARRAY_BUFFER, mVbo);
71 glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3u, &kVertexData[0], GL_STATIC_DRAW);
72
73 const unsigned int kIndices[3] = {0u, 1u, 2u};
74 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo);
75 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 3, &kIndices[0],
76 GL_STATIC_DRAW);
77 ASSERT_GL_NO_ERROR();
78 }
79
80 GLTexture mTex2d;
81 GLVertexArray mVao;
82 GLBuffer mVbo;
83 GLBuffer mIbo;
84 GLFramebuffer mFramebuffer;
85};
86
Martin Radev8f276e22017-05-30 12:05:52 +030087class MultiviewSideBySideRenderTest : public MultiviewDrawTest
88{
89 protected:
90 void createFBO(int width, int height, int numViews)
91 {
92 // Assert that width is a multiple of numViews.
93 ASSERT_TRUE(width % numViews == 0);
94 const int widthPerView = width / numViews;
95
96 // Create color and depth textures.
97 glBindTexture(GL_TEXTURE_2D, mColorTexture);
98 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
99 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
100 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
101 ASSERT_GL_NO_ERROR();
102
103 glBindTexture(GL_TEXTURE_2D, mDepthTexture);
104 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT,
105 GL_FLOAT, NULL);
106 glBindTexture(GL_TEXTURE_2D, 0);
107 ASSERT_GL_NO_ERROR();
108
109 // Create draw framebuffer to be used for side-by-side rendering.
110 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDrawFramebuffer);
111 std::vector<GLint> viewportOffsets(numViews * 2u);
112 for (int i = 0u; i < numViews; ++i)
113 {
114 viewportOffsets[i * 2] = i * widthPerView;
115 viewportOffsets[i * 2 + 1] = 0;
116 }
117 glFramebufferTextureMultiviewSideBySideANGLE(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
118 mColorTexture, 0, numViews,
119 &viewportOffsets[0]);
120 glFramebufferTextureMultiviewSideBySideANGLE(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
121 mDepthTexture, 0, numViews,
122 &viewportOffsets[0]);
123
124 GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
125 glDrawBuffers(1, DrawBuffers);
126 ASSERT_GL_NO_ERROR();
127 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
128
129 // Create read framebuffer to be used to retrieve the pixel information for testing
130 // purposes.
131 glBindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer);
132 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
133 mColorTexture, 0);
134 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_READ_FRAMEBUFFER));
135
136 // Clear the buffers.
137 glViewport(0, 0, width, height);
138 glScissor(0, 0, width, height);
139 glClearColor(0, 0, 0, 0);
140 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
141
142 // Set viewport and scissor of each view.
143 glViewport(0, 0, widthPerView, height);
144 glScissor(0, 0, widthPerView, height);
145 }
146
147 GLTexture mColorTexture;
148 GLTexture mDepthTexture;
149 GLFramebuffer mDrawFramebuffer;
150 GLFramebuffer mReadFramebuffer;
151};
152
153class MultiviewSideBySideRenderDualViewTest : public MultiviewSideBySideRenderTest
154{
155 protected:
156 MultiviewSideBySideRenderDualViewTest() : mProgram(0u) {}
157 ~MultiviewSideBySideRenderDualViewTest()
158 {
159 if (mProgram != 0u)
160 {
161 glDeleteProgram(mProgram);
162 }
163 }
164
165 void SetUp() override
166 {
167 MultiviewSideBySideRenderTest::SetUp();
168
169 if (!requestMultiviewExtension())
170 {
171 return;
172 }
173
174 const std::string vsSource =
175 "#version 300 es\n"
176 "#extension GL_OVR_multiview : require\n"
177 "layout(num_views = 2) in;\n"
178 "in vec4 vPosition;\n"
179 "void main()\n"
180 "{\n"
181 " gl_Position.x = (gl_ViewID_OVR == 0u ? vPosition.x*0.5 + 0.5 : vPosition.x*0.5);\n"
182 " gl_Position.yzw = vPosition.yzw;\n"
183 "}\n";
184
185 const std::string fsSource =
186 "#version 300 es\n"
187 "#extension GL_OVR_multiview : require\n"
188 "precision mediump float;\n"
189 "out vec4 col;\n"
190 "void main()\n"
191 "{\n"
192 " col = vec4(1,0,0,0);\n"
193 "}\n";
194
195 createFBO(4, 1, 2);
196 createProgram(vsSource, fsSource);
197 }
198
199 void createProgram(const std::string &vs, const std::string &fs)
200 {
201 mProgram = CompileProgram(vs, fs);
202 if (mProgram == 0)
203 {
204 FAIL() << "shader compilation failed.";
205 }
206 glUseProgram(mProgram);
207 ASSERT_GL_NO_ERROR();
208 }
209
210 void checkOutput()
211 {
212 EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
213 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
214 EXPECT_PIXEL_EQ(2, 0, 255, 0, 0, 0);
215 EXPECT_PIXEL_EQ(3, 0, 0, 0, 0, 0);
216 }
217
218 GLuint mProgram;
219};
220
Martin Radev0d671c92017-08-10 16:41:52 +0300221class MultiviewSideBySideOcclusionQueryTest : public MultiviewSideBySideRenderTest
222{
223 protected:
224 MultiviewSideBySideOcclusionQueryTest() {}
225
226 GLuint drawAndRetrieveOcclusionQueryResult(GLuint program)
227 {
228 GLuint query;
229 glGenQueries(1, &query);
230 glBeginQuery(GL_ANY_SAMPLES_PASSED, query);
231 drawQuad(program, "vPosition", 0.0f, 1.0f, true);
232 glEndQueryEXT(GL_ANY_SAMPLES_PASSED);
233
234 GLuint result = GL_TRUE;
235 glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result);
236 return result;
237 }
238};
239
Martin Radev14a26ae2017-07-24 15:56:29 +0300240// The test verifies that glDraw*Indirect:
241// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
242// than 1.
243// 2) does not generate any error if the draw framebuffer has exactly 1 view.
Martin Radev7cf61662017-07-26 17:10:53 +0300244TEST_P(MultiviewDrawValidationTest, IndirectDraw)
Martin Radev14a26ae2017-07-24 15:56:29 +0300245{
246 if (!requestMultiviewExtension())
247 {
248 return;
249 }
250
Martin Radev14a26ae2017-07-24 15:56:29 +0300251 const GLint viewportOffsets[4] = {0, 0, 2, 0};
Martin Radev14a26ae2017-07-24 15:56:29 +0300252
253 const std::string fsSource =
254 "#version 300 es\n"
255 "#extension GL_OVR_multiview : require\n"
256 "precision mediump float;\n"
257 "void main()\n"
258 "{}\n";
259
Martin Radev14a26ae2017-07-24 15:56:29 +0300260 GLBuffer commandBuffer;
261 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer);
262 const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u};
263 glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW);
264 ASSERT_GL_NO_ERROR();
265
Martin Radev14a26ae2017-07-24 15:56:29 +0300266 // Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views.
267 {
268 const std::string &vsSource =
269 "#version 300 es\n"
270 "#extension GL_OVR_multiview : require\n"
271 "layout(num_views = 2) in;\n"
272 "void main()\n"
273 "{}\n";
274 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
275 glUseProgram(program);
276
Martin Radev7cf61662017-07-26 17:10:53 +0300277 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
278 0, 2, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300279
280 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
281 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
282
283 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
284 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
285 }
286
287 // Check that no errors are generated if the number of views is 1.
288 {
289 const std::string &vsSource =
290 "#version 300 es\n"
291 "#extension GL_OVR_multiview : require\n"
292 "layout(num_views = 1) in;\n"
293 "void main()\n"
294 "{}\n";
295 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
296 glUseProgram(program);
297
Martin Radev7cf61662017-07-26 17:10:53 +0300298 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
299 0, 1, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300300
301 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
302 EXPECT_GL_NO_ERROR();
303
304 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
305 EXPECT_GL_NO_ERROR();
306 }
307}
308
Martin Radev7cf61662017-07-26 17:10:53 +0300309// The test verifies that glDraw*:
310// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer and
311// program differs.
312// 2) does not generate any error if the number of views is the same.
313// 3) does not generate any error if the program does not use the multiview extension.
314TEST_P(MultiviewDrawValidationTest, NumViewsMismatch)
315{
316 if (!requestMultiviewExtension())
317 {
318 return;
319 }
320
321 const GLint viewportOffsets[4] = {0, 0, 2, 0};
322
323 const std::string &vsSource =
324 "#version 300 es\n"
325 "#extension GL_OVR_multiview : require\n"
326 "layout(num_views = 2) in;\n"
327 "void main()\n"
328 "{}\n";
329 const std::string &fsSource =
330 "#version 300 es\n"
331 "#extension GL_OVR_multiview : require\n"
332 "precision mediump float;\n"
333 "void main()\n"
334 "{}\n";
335 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
336 glUseProgram(program);
337
338 // Check for a GL_INVALID_OPERATION error with the framebuffer and program having different
339 // number of views.
340 {
341 // The framebuffer has only 1 view.
342 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
343 0, 1, &viewportOffsets[0]);
344
345 glDrawArrays(GL_TRIANGLES, 0, 3);
346 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
347
348 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
349 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
350 }
351
352 // Check that no errors are generated if the number of views in both program and draw
353 // framebuffer matches.
354 {
355 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
356 0, 2, &viewportOffsets[0]);
357
358 glDrawArrays(GL_TRIANGLES, 0, 3);
359 EXPECT_GL_NO_ERROR();
360
361 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
362 EXPECT_GL_NO_ERROR();
363 }
364
365 // Check that no errors are generated if the program does not use the multiview extension.
366 {
367 const std::string &vsSourceNoMultiview =
368 "#version 300 es\n"
369 "void main()\n"
370 "{}\n";
371 const std::string &fsSourceNoMultiview =
372 "#version 300 es\n"
373 "precision mediump float;\n"
374 "void main()\n"
375 "{}\n";
376 ANGLE_GL_PROGRAM(programNoMultiview, vsSourceNoMultiview, fsSourceNoMultiview);
377 glUseProgram(programNoMultiview);
378
379 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
380 0, 2, &viewportOffsets[0]);
381
382 glDrawArrays(GL_TRIANGLES, 0, 3);
383 EXPECT_GL_NO_ERROR();
384
385 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
386 EXPECT_GL_NO_ERROR();
387 }
388}
389
Martin Radev7e69f762017-07-27 14:54:13 +0300390// The test verifies that glDraw*:
391// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
392// greater than 1 and there is an active transform feedback object.
393// 2) does not generate any error if the number of views in the draw framebuffer is 1.
394TEST_P(MultiviewDrawValidationTest, ActiveTransformFeedback)
395{
396 if (!requestMultiviewExtension())
397 {
398 return;
399 }
400
401 const GLint viewportOffsets[4] = {0, 0, 2, 0};
402
403 const std::string &vsSource =
404 "#version 300 es\n"
405 "void main()\n"
406 "{}\n";
407 const std::string &fsSource =
408 "#version 300 es\n"
409 "precision mediump float;\n"
410 "void main()\n"
411 "{}\n";
412 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
413 glUseProgram(program);
414
415 GLBuffer tbo;
416 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);
417 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 4u, nullptr, GL_STATIC_DRAW);
418
419 GLTransformFeedback transformFeedback;
420 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedback);
421 glBeginTransformFeedback(GL_TRIANGLES);
422 ASSERT_GL_NO_ERROR();
423
424 // Check that drawArrays generates an error when there is an active transform feedback object
425 // and the number of views in the draw framebuffer is greater than 1.
426 {
427 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
428 0, 2, &viewportOffsets[0]);
429 glDrawArrays(GL_TRIANGLES, 0, 3);
430 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
431 }
432
433 // Check that drawArrays does not generate an error when the number of views in the draw
434 // framebuffer is 1.
435 {
436 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
437 0, 1, &viewportOffsets[0]);
438 glDrawArrays(GL_TRIANGLES, 0, 3);
439 EXPECT_GL_NO_ERROR();
440 }
441
442 glEndTransformFeedback();
443}
444
Martin Radevffe754b2017-07-31 10:38:07 +0300445// The test verifies that glDraw*:
446// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
447// greater than 1 and there is an active query for target GL_TIME_ELAPSED_EXT.
448// 2) does not generate any error if the number of views in the draw framebuffer is 1.
449TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery)
450{
451 if (!requestMultiviewExtension())
452 {
453 return;
454 }
455
456 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
457 {
458 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
459 << std::endl;
460 return;
461 }
462
463 const GLint viewportOffsets[4] = {0, 0, 2, 0};
464 const std::string &vsSource =
465 "#version 300 es\n"
466 "void main()\n"
467 "{}\n";
468 const std::string &fsSource =
469 "#version 300 es\n"
470 "precision mediump float;\n"
471 "void main()\n"
472 "{}\n";
473 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
474 glUseProgram(program);
475
476 GLuint query = 0u;
477 glGenQueriesEXT(1, &query);
478 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
479
480 // Check first case.
481 {
482 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
483 0, 2, &viewportOffsets[0]);
484 glDrawArrays(GL_TRIANGLES, 0, 3);
485 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
486 }
487
488 // Check second case.
489 {
490 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
491 0, 1, &viewportOffsets[0]);
492 glDrawArrays(GL_TRIANGLES, 0, 3);
493 EXPECT_GL_NO_ERROR();
494 }
495
496 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
497 glDeleteQueries(1, &query);
498}
499
Martin Radev8f276e22017-05-30 12:05:52 +0300500// The test checks that glDrawArrays can be used to render into two views.
501TEST_P(MultiviewSideBySideRenderDualViewTest, DrawArrays)
502{
503 if (!requestMultiviewExtension())
504 {
505 return;
506 }
507 drawQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
508 ASSERT_GL_NO_ERROR();
509
510 checkOutput();
511}
512
513// The test checks that glDrawElements can be used to render into two views.
514TEST_P(MultiviewSideBySideRenderDualViewTest, DrawElements)
515{
516 if (!requestMultiviewExtension())
517 {
518 return;
519 }
520 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
521 ASSERT_GL_NO_ERROR();
522
523 checkOutput();
524}
525
526// The test checks that glDrawRangeElements can be used to render into two views.
527TEST_P(MultiviewSideBySideRenderDualViewTest, DrawRangeElements)
528{
529 if (!requestMultiviewExtension())
530 {
531 return;
532 }
533 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true, true);
534 ASSERT_GL_NO_ERROR();
535
536 checkOutput();
537}
538
539// The test checks that glDrawArrays can be used to render into four views.
540TEST_P(MultiviewSideBySideRenderTest, DrawArraysFourViews)
541{
542 if (!requestMultiviewExtension())
543 {
544 return;
545 }
546
547 const std::string vsSource =
548 "#version 300 es\n"
549 "#extension GL_OVR_multiview2 : require\n"
550 "layout(num_views = 4) in;\n"
551 "in vec4 vPosition;\n"
552 "void main()\n"
553 "{\n"
554 " if (gl_ViewID_OVR == 0u) {\n"
555 " gl_Position.x = vPosition.x*0.25 - 0.75;\n"
556 " } else if (gl_ViewID_OVR == 1u) {\n"
557 " gl_Position.x = vPosition.x*0.25 - 0.25;\n"
558 " } else if (gl_ViewID_OVR == 2u) {\n"
559 " gl_Position.x = vPosition.x*0.25 + 0.25;\n"
560 " } else {\n"
561 " gl_Position.x = vPosition.x*0.25 + 0.75;\n"
562 " }"
563 " gl_Position.yzw = vPosition.yzw;\n"
564 "}\n";
565
566 const std::string fsSource =
567 "#version 300 es\n"
568 "#extension GL_OVR_multiview2 : require\n"
569 "precision mediump float;\n"
570 "out vec4 col;\n"
571 "void main()\n"
572 "{\n"
573 " col = vec4(1,0,0,0);\n"
574 "}\n";
575
576 createFBO(16, 1, 4);
577 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
578 glUseProgram(program);
579
580 drawQuad(program, "vPosition", 0.0f, 1.0f, true);
581 ASSERT_GL_NO_ERROR();
582
583 for (int i = 0; i < 4; ++i)
584 {
585 for (int j = 0; j < 4; ++j)
586 {
587 const int arrayIndex = i * 4 + j;
588 if (i == j)
589 {
590 EXPECT_PIXEL_EQ(arrayIndex, 0, 255, 0, 0, 0);
591 }
592 else
593 {
594 EXPECT_PIXEL_EQ(arrayIndex, 0, 0, 0, 0, 0);
595 }
596 }
597 }
598 EXPECT_GL_NO_ERROR();
599}
600
601// The test checks that glDrawArraysInstanced can be used to render into two views.
602TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced)
603{
604 if (!requestMultiviewExtension())
605 {
606 return;
607 }
608
609 const std::string vsSource =
610 "#version 300 es\n"
611 "#extension GL_OVR_multiview : require\n"
612 "layout(num_views = 2) in;\n"
613 "in vec4 vPosition;\n"
614 "void main()\n"
615 "{\n"
616 " vec4 p = vPosition;\n"
617 " if (gl_InstanceID == 1){\n"
618 " p.y = .5*p.y + .5;\n"
619 " } else {\n"
620 " p.y = p.y*.5;\n"
621 " }\n"
622 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x*0.5 + 0.5 : p.x*0.5);\n"
623 " gl_Position.yzw = p.yzw;\n"
624 "}\n";
625
626 const std::string fsSource =
627 "#version 300 es\n"
628 "#extension GL_OVR_multiview : require\n"
629 "precision mediump float;\n"
630 "out vec4 col;\n"
631 "void main()\n"
632 "{\n"
633 " col = vec4(1,0,0,0);\n"
634 "}\n";
635
636 createFBO(4, 2, 2);
637 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
638 glUseProgram(program);
639
640 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 2);
641 ASSERT_GL_NO_ERROR();
642
643 const GLubyte expectedResult[4][8] = {
644 {0, 255, 255, 0}, {0, 255, 255, 0},
645 };
646 for (int row = 0; row < 2; ++row)
647 {
648 for (int col = 0; col < 4; ++col)
649 {
650 EXPECT_PIXEL_EQ(col, row, expectedResult[row][col], 0, 0, 0);
651 }
652 }
653}
654
Martin Radev553590a2017-07-31 16:40:39 +0300655// The test verifies that the attribute divisor is correctly adjusted when drawing with a multi-view
656// program. The test draws 4 instances of a quad each of which covers a single pixel. The x and y
657// offset of each quad are passed as separate attributes which are indexed based on the
658// corresponding attribute divisors. A divisor of 1 is used for the y offset to have all quads
659// drawn vertically next to each other. A divisor of 3 is used for the x offset to have the last
660// quad offsetted by one pixel to the right. Note that the number of views is divisible by 1, but
661// not by 3.
662TEST_P(MultiviewSideBySideRenderTest, AttribDivisor)
663{
664 if (!requestMultiviewExtension())
665 {
666 return;
667 }
668
669 const std::string &vsSource =
670 "#version 300 es\n"
671 "#extension GL_OVR_multiview2 : require\n"
672 "layout(num_views = 2) in;\n"
673 "in vec3 vPosition;\n"
674 "in float offsetX;\n"
675 "in float offsetY;\n"
676 "void main()\n"
677 "{\n"
678 " vec4 p = vec4(vPosition, 1.);\n"
679 " p.xy = p.xy * 0.25 - 0.75 + vec2(offsetX, offsetY);\n"
680 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x : p.x + 1.0);\n"
681 " gl_Position.yzw = p.yzw;\n"
682 "}\n";
683
684 const std::string &fsSource =
685 "#version 300 es\n"
686 "#extension GL_OVR_multiview2 : require\n"
687 "precision mediump float;\n"
688 "out vec4 col;\n"
689 "void main()\n"
690 "{\n"
691 " col = vec4(1,0,0,0);\n"
692 "}\n";
693 createFBO(8, 4, 2);
694 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
695 glUseProgram(program);
696
697 GLBuffer xOffsetVBO;
698 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
699 const GLfloat xOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.0f};
700 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, xOffsetData, GL_STATIC_DRAW);
701 GLint xOffsetLoc = glGetAttribLocation(program, "offsetX");
702 glVertexAttribPointer(xOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
703 glVertexAttribDivisor(xOffsetLoc, 3);
704 glEnableVertexAttribArray(xOffsetLoc);
705
706 GLBuffer yOffsetVBO;
707 glBindBuffer(GL_ARRAY_BUFFER, yOffsetVBO);
708 const GLfloat yOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.5f};
709 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, yOffsetData, GL_STATIC_DRAW);
710 GLint yOffsetLoc = glGetAttribLocation(program, "offsetY");
711 glVertexAttribDivisor(yOffsetLoc, 1);
712 glVertexAttribPointer(yOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
713 glEnableVertexAttribArray(yOffsetLoc);
714
715 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 4);
716 ASSERT_GL_NO_ERROR();
717
718 const GLubyte expectedRedChannel[4][8] = {{255, 0, 0, 0, 0, 0, 255, 0},
719 {255, 0, 0, 0, 0, 0, 255, 0},
720 {255, 0, 0, 0, 0, 0, 255, 0},
721 {0, 255, 0, 0, 0, 0, 0, 255}};
722 for (int row = 0; row < 4; ++row)
723 {
724 for (int col = 0; col < 8; ++col)
725 {
726 EXPECT_PIXEL_EQ(col, row, expectedRedChannel[row][col], 0, 0, 0);
727 }
728 }
729}
730
731// Test that different sequences of vertexAttribDivisor, useProgram and bindVertexArray in a
732// multi-view context propagate the correct divisor to the driver.
733TEST_P(MultiviewSideBySideRenderTest, DivisorOrderOfOperation)
734{
735 if (!requestMultiviewExtension())
736 {
737 return;
738 }
739
740 createFBO(2, 1, 2);
741
742 // Create multiview program.
743 const std::string &vs =
744 "#version 300 es\n"
745 "#extension GL_OVR_multiview2 : require\n"
746 "layout(num_views = 2) in;\n"
747 "layout(location = 0) in vec2 vPosition;\n"
748 "layout(location = 1) in float offsetX;\n"
749 "void main()\n"
750 "{\n"
751 " vec4 p = vec4(vPosition, 0.0, 1.0);\n"
752 " p.x += offsetX;\n"
753 " gl_Position = p;\n"
754 "}\n";
755
756 const std::string &fs =
757 "#version 300 es\n"
758 "#extension GL_OVR_multiview2 : require\n"
759 "precision mediump float;\n"
760 "out vec4 col;\n"
761 "void main()\n"
762 "{\n"
763 " col = vec4(1,0,0,0);\n"
764 "}\n";
765
766 ANGLE_GL_PROGRAM(program, vs, fs);
767
768 const std::string &dummyVS =
769 "#version 300 es\n"
770 "layout(location = 0) in vec2 vPosition;\n"
771 "layout(location = 1) in float offsetX;\n"
772 "void main()\n"
773 "{\n"
774 " gl_Position = vec4(vPosition, 0.0, 1.0);\n"
775 "}\n";
776
777 const std::string &dummyFS =
778 "#version 300 es\n"
779 "precision mediump float;\n"
780 "out vec4 col;\n"
781 "void main()\n"
782 "{\n"
783 " col = vec4(0,0,0,0);\n"
784 "}\n";
785
786 ANGLE_GL_PROGRAM(dummyProgram, dummyVS, dummyFS);
787
788 GLBuffer xOffsetVBO;
789 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
790 const GLfloat xOffsetData[12] = {0.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f,
791 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f};
792 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 12, xOffsetData, GL_STATIC_DRAW);
793
794 GLBuffer vertexVBO;
795 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
796 Vector2 kQuadVertices[6] = {Vector2(-1.f, -1.f), Vector2(1.f, -1.f), Vector2(1.f, 1.f),
797 Vector2(-1.f, -1.f), Vector2(1.f, 1.f), Vector2(-1.f, 1.f)};
798 glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
799
800 GLVertexArray vao[2];
801 for (size_t i = 0u; i < 2u; ++i)
802 {
803 glBindVertexArray(vao[i]);
804
805 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
806 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
807 glEnableVertexAttribArray(0);
808
809 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
810 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
811 glEnableVertexAttribArray(1);
812 }
813 ASSERT_GL_NO_ERROR();
814
815 glViewport(0, 0, 1, 1);
816 glScissor(0, 0, 1, 1);
817 glClearColor(0, 0, 0, 0);
818
819 // Clear the buffers, propagate divisor to the driver, bind the vao and keep it active.
820 // It is necessary to call draw, so that the divisor is propagated and to guarantee that dirty
821 // bits are cleared.
822 glUseProgram(dummyProgram);
823 glBindVertexArray(vao[0]);
824 glVertexAttribDivisor(1, 0);
825 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
826 glUseProgram(0);
827 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
828 ASSERT_GL_NO_ERROR();
829
830 // Check that vertexAttribDivisor uses the number of views to update the divisor.
831 glUseProgram(program);
832 glVertexAttribDivisor(1, 1);
833 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
834 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
835 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
836
837 // Clear the buffers and propagate divisor to the driver.
838 // We keep the vao active and propagate the divisor to guarantee that there are no unresolved
839 // dirty bits when useProgram is called.
840 glUseProgram(dummyProgram);
841 glVertexAttribDivisor(1, 1);
842 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
843 glUseProgram(0);
844 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
845 ASSERT_GL_NO_ERROR();
846
847 // Check that useProgram uses the number of views to update the divisor.
848 glUseProgram(program);
849 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
850 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
851 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
852
853 // We go through similar steps as before.
854 glUseProgram(dummyProgram);
855 glVertexAttribDivisor(1, 1);
856 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
857 glUseProgram(0);
858 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
859 ASSERT_GL_NO_ERROR();
860
861 // Check that bindVertexArray uses the number of views to update the divisor.
862 {
863 // Call useProgram with vao[1] being active to guarantee that useProgram will adjust the
864 // divisor for vao[1] only.
865 glBindVertexArray(vao[1]);
866 glUseProgram(program);
867 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
868 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
869 glBindVertexArray(0);
870 ASSERT_GL_NO_ERROR();
871 }
872 // Bind vao[0] after useProgram is called to ensure that bindVertexArray is the call which
873 // adjusts the divisor.
874 glBindVertexArray(vao[0]);
875 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
876 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
877 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
878}
879
Martin Radev0d671c92017-08-10 16:41:52 +0300880// Test that no fragments pass the occlusion query for a multi-view vertex shader which always
881// transforms geometry to be outside of the clip region.
882TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryNothingVisible)
883{
884 if (!requestMultiviewExtension())
885 {
886 return;
887 }
888
889 const std::string vsSource =
890 "#version 300 es\n"
891 "#extension GL_OVR_multiview : require\n"
892 "layout(num_views = 2) in;\n"
893 "in vec3 vPosition;\n"
894 "void main()\n"
895 "{\n"
896 " gl_Position.x = 2.0;\n"
897 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
898 "}\n";
899
900 const std::string fsSource =
901 "#version 300 es\n"
902 "#extension GL_OVR_multiview : require\n"
903 "precision mediump float;\n"
904 "out vec4 col;\n"
905 "void main()\n"
906 "{\n"
907 " col = vec4(1,0,0,0);\n"
908 "}\n";
909 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
910 glUseProgram(program);
911 createFBO(2, 1, 2);
912
913 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
914 ASSERT_GL_NO_ERROR();
915 EXPECT_GL_FALSE(result);
916}
917
918// Test that there are fragments passing the occlusion query if only view 0 can produce
919// output.
920TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryOnlyLeftVisible)
921{
922 if (!requestMultiviewExtension())
923 {
924 return;
925 }
926
927 const std::string vsSource =
928 "#version 300 es\n"
929 "#extension GL_OVR_multiview : require\n"
930 "layout(num_views = 2) in;\n"
931 "in vec3 vPosition;\n"
932 "void main()\n"
933 "{\n"
934 " gl_Position.x = gl_ViewID_OVR == 0u ? vPosition.x : 2.0;\n"
935 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
936 "}\n";
937
938 const std::string fsSource =
939 "#version 300 es\n"
940 "#extension GL_OVR_multiview : require\n"
941 "precision mediump float;\n"
942 "out vec4 col;\n"
943 "void main()\n"
944 "{\n"
945 " col = vec4(1,0,0,0);\n"
946 "}\n";
947 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
948 glUseProgram(program);
949 createFBO(2, 1, 2);
950
951 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
952 ASSERT_GL_NO_ERROR();
953 EXPECT_GL_TRUE(result);
954}
955
956// Test that there are fragments passing the occlusion query if only view 1 can produce
957// output.
958TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryOnlyRightVisible)
959{
960 if (!requestMultiviewExtension())
961 {
962 return;
963 }
964
965 const std::string vsSource =
966 "#version 300 es\n"
967 "#extension GL_OVR_multiview : require\n"
968 "layout(num_views = 2) in;\n"
969 "in vec3 vPosition;\n"
970 "void main()\n"
971 "{\n"
972 " gl_Position.x = gl_ViewID_OVR == 1u ? vPosition.x : 2.0;\n"
973 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
974 "}\n";
975
976 const std::string fsSource =
977 "#version 300 es\n"
978 "#extension GL_OVR_multiview : require\n"
979 "precision mediump float;\n"
980 "out vec4 col;\n"
981 "void main()\n"
982 "{\n"
983 " col = vec4(1,0,0,0);\n"
984 "}\n";
985 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
986 glUseProgram(program);
987 createFBO(2, 1, 2);
988
989 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
990 ASSERT_GL_NO_ERROR();
991 EXPECT_GL_TRUE(result);
992}
993
Martin Radev8f276e22017-05-30 12:05:52 +0300994ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
995ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
Martin Radev0d671c92017-08-10 16:41:52 +0300996ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
997ANGLE_INSTANTIATE_TEST(MultiviewSideBySideOcclusionQueryTest, ES3_OPENGL());