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