blob: bb7453f08188f08a51c447874e2e297f22228f78 [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 Radev41ac68e2017-06-06 12:16:58 +0300241class MultiviewProgramGenerationTest : public MultiviewSideBySideRenderTest
242{
243 protected:
244 MultiviewProgramGenerationTest() {}
245};
246
Martin Radev14a26ae2017-07-24 15:56:29 +0300247// The test verifies that glDraw*Indirect:
248// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
249// than 1.
250// 2) does not generate any error if the draw framebuffer has exactly 1 view.
Martin Radev7cf61662017-07-26 17:10:53 +0300251TEST_P(MultiviewDrawValidationTest, IndirectDraw)
Martin Radev14a26ae2017-07-24 15:56:29 +0300252{
253 if (!requestMultiviewExtension())
254 {
255 return;
256 }
257
Martin Radev14a26ae2017-07-24 15:56:29 +0300258 const GLint viewportOffsets[4] = {0, 0, 2, 0};
Martin Radev14a26ae2017-07-24 15:56:29 +0300259
260 const std::string fsSource =
261 "#version 300 es\n"
262 "#extension GL_OVR_multiview : require\n"
263 "precision mediump float;\n"
264 "void main()\n"
265 "{}\n";
266
Martin Radev14a26ae2017-07-24 15:56:29 +0300267 GLBuffer commandBuffer;
268 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer);
269 const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u};
270 glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW);
271 ASSERT_GL_NO_ERROR();
272
Martin Radev14a26ae2017-07-24 15:56:29 +0300273 // Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views.
274 {
275 const std::string &vsSource =
276 "#version 300 es\n"
277 "#extension GL_OVR_multiview : require\n"
278 "layout(num_views = 2) in;\n"
279 "void main()\n"
280 "{}\n";
281 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
282 glUseProgram(program);
283
Martin Radev7cf61662017-07-26 17:10:53 +0300284 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
285 0, 2, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300286
287 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
288 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
289
290 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
291 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
292 }
293
294 // Check that no errors are generated if the number of views is 1.
295 {
296 const std::string &vsSource =
297 "#version 300 es\n"
298 "#extension GL_OVR_multiview : require\n"
299 "layout(num_views = 1) in;\n"
300 "void main()\n"
301 "{}\n";
302 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
303 glUseProgram(program);
304
Martin Radev7cf61662017-07-26 17:10:53 +0300305 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
306 0, 1, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300307
308 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
309 EXPECT_GL_NO_ERROR();
310
311 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
312 EXPECT_GL_NO_ERROR();
313 }
314}
315
Martin Radev7cf61662017-07-26 17:10:53 +0300316// The test verifies that glDraw*:
317// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer and
318// program differs.
319// 2) does not generate any error if the number of views is the same.
320// 3) does not generate any error if the program does not use the multiview extension.
321TEST_P(MultiviewDrawValidationTest, NumViewsMismatch)
322{
323 if (!requestMultiviewExtension())
324 {
325 return;
326 }
327
328 const GLint viewportOffsets[4] = {0, 0, 2, 0};
329
330 const std::string &vsSource =
331 "#version 300 es\n"
332 "#extension GL_OVR_multiview : require\n"
333 "layout(num_views = 2) in;\n"
334 "void main()\n"
335 "{}\n";
336 const std::string &fsSource =
337 "#version 300 es\n"
338 "#extension GL_OVR_multiview : require\n"
339 "precision mediump float;\n"
340 "void main()\n"
341 "{}\n";
342 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
343 glUseProgram(program);
344
345 // Check for a GL_INVALID_OPERATION error with the framebuffer and program having different
346 // number of views.
347 {
348 // The framebuffer has only 1 view.
349 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
350 0, 1, &viewportOffsets[0]);
351
352 glDrawArrays(GL_TRIANGLES, 0, 3);
353 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
354
355 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
356 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
357 }
358
359 // Check that no errors are generated if the number of views in both program and draw
360 // framebuffer matches.
361 {
362 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
363 0, 2, &viewportOffsets[0]);
364
365 glDrawArrays(GL_TRIANGLES, 0, 3);
366 EXPECT_GL_NO_ERROR();
367
368 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
369 EXPECT_GL_NO_ERROR();
370 }
371
372 // Check that no errors are generated if the program does not use the multiview extension.
373 {
374 const std::string &vsSourceNoMultiview =
375 "#version 300 es\n"
376 "void main()\n"
377 "{}\n";
378 const std::string &fsSourceNoMultiview =
379 "#version 300 es\n"
380 "precision mediump float;\n"
381 "void main()\n"
382 "{}\n";
383 ANGLE_GL_PROGRAM(programNoMultiview, vsSourceNoMultiview, fsSourceNoMultiview);
384 glUseProgram(programNoMultiview);
385
386 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
387 0, 2, &viewportOffsets[0]);
388
389 glDrawArrays(GL_TRIANGLES, 0, 3);
390 EXPECT_GL_NO_ERROR();
391
392 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
393 EXPECT_GL_NO_ERROR();
394 }
395}
396
Martin Radev7e69f762017-07-27 14:54:13 +0300397// The test verifies that glDraw*:
398// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
399// greater than 1 and there is an active transform feedback object.
400// 2) does not generate any error if the number of views in the draw framebuffer is 1.
401TEST_P(MultiviewDrawValidationTest, ActiveTransformFeedback)
402{
403 if (!requestMultiviewExtension())
404 {
405 return;
406 }
407
408 const GLint viewportOffsets[4] = {0, 0, 2, 0};
409
410 const std::string &vsSource =
411 "#version 300 es\n"
412 "void main()\n"
413 "{}\n";
414 const std::string &fsSource =
415 "#version 300 es\n"
416 "precision mediump float;\n"
417 "void main()\n"
418 "{}\n";
419 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
420 glUseProgram(program);
421
422 GLBuffer tbo;
423 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);
424 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 4u, nullptr, GL_STATIC_DRAW);
425
426 GLTransformFeedback transformFeedback;
427 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedback);
428 glBeginTransformFeedback(GL_TRIANGLES);
429 ASSERT_GL_NO_ERROR();
430
431 // Check that drawArrays generates an error when there is an active transform feedback object
432 // and the number of views in the draw framebuffer is greater than 1.
433 {
434 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
435 0, 2, &viewportOffsets[0]);
436 glDrawArrays(GL_TRIANGLES, 0, 3);
437 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
438 }
439
440 // Check that drawArrays does not generate an error when the number of views in the draw
441 // framebuffer is 1.
442 {
443 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
444 0, 1, &viewportOffsets[0]);
445 glDrawArrays(GL_TRIANGLES, 0, 3);
446 EXPECT_GL_NO_ERROR();
447 }
448
449 glEndTransformFeedback();
450}
451
Martin Radevffe754b2017-07-31 10:38:07 +0300452// The test verifies that glDraw*:
453// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
454// greater than 1 and there is an active query for target GL_TIME_ELAPSED_EXT.
455// 2) does not generate any error if the number of views in the draw framebuffer is 1.
456TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery)
457{
458 if (!requestMultiviewExtension())
459 {
460 return;
461 }
462
463 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
464 {
465 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
466 << std::endl;
467 return;
468 }
469
470 const GLint viewportOffsets[4] = {0, 0, 2, 0};
471 const std::string &vsSource =
472 "#version 300 es\n"
473 "void main()\n"
474 "{}\n";
475 const std::string &fsSource =
476 "#version 300 es\n"
477 "precision mediump float;\n"
478 "void main()\n"
479 "{}\n";
480 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
481 glUseProgram(program);
482
483 GLuint query = 0u;
484 glGenQueriesEXT(1, &query);
485 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
486
487 // Check first case.
488 {
489 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
490 0, 2, &viewportOffsets[0]);
491 glDrawArrays(GL_TRIANGLES, 0, 3);
492 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
493 }
494
495 // Check second case.
496 {
497 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
498 0, 1, &viewportOffsets[0]);
499 glDrawArrays(GL_TRIANGLES, 0, 3);
500 EXPECT_GL_NO_ERROR();
501 }
502
503 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
504 glDeleteQueries(1, &query);
505}
506
Martin Radev8f276e22017-05-30 12:05:52 +0300507// The test checks that glDrawArrays can be used to render into two views.
508TEST_P(MultiviewSideBySideRenderDualViewTest, DrawArrays)
509{
510 if (!requestMultiviewExtension())
511 {
512 return;
513 }
514 drawQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
515 ASSERT_GL_NO_ERROR();
516
517 checkOutput();
518}
519
520// The test checks that glDrawElements can be used to render into two views.
521TEST_P(MultiviewSideBySideRenderDualViewTest, DrawElements)
522{
523 if (!requestMultiviewExtension())
524 {
525 return;
526 }
527 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
528 ASSERT_GL_NO_ERROR();
529
530 checkOutput();
531}
532
533// The test checks that glDrawRangeElements can be used to render into two views.
534TEST_P(MultiviewSideBySideRenderDualViewTest, DrawRangeElements)
535{
536 if (!requestMultiviewExtension())
537 {
538 return;
539 }
540 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true, true);
541 ASSERT_GL_NO_ERROR();
542
543 checkOutput();
544}
545
546// The test checks that glDrawArrays can be used to render into four views.
547TEST_P(MultiviewSideBySideRenderTest, DrawArraysFourViews)
548{
549 if (!requestMultiviewExtension())
550 {
551 return;
552 }
553
554 const std::string vsSource =
555 "#version 300 es\n"
556 "#extension GL_OVR_multiview2 : require\n"
557 "layout(num_views = 4) in;\n"
558 "in vec4 vPosition;\n"
559 "void main()\n"
560 "{\n"
561 " if (gl_ViewID_OVR == 0u) {\n"
562 " gl_Position.x = vPosition.x*0.25 - 0.75;\n"
563 " } else if (gl_ViewID_OVR == 1u) {\n"
564 " gl_Position.x = vPosition.x*0.25 - 0.25;\n"
565 " } else if (gl_ViewID_OVR == 2u) {\n"
566 " gl_Position.x = vPosition.x*0.25 + 0.25;\n"
567 " } else {\n"
568 " gl_Position.x = vPosition.x*0.25 + 0.75;\n"
569 " }"
570 " gl_Position.yzw = vPosition.yzw;\n"
571 "}\n";
572
573 const std::string fsSource =
574 "#version 300 es\n"
575 "#extension GL_OVR_multiview2 : require\n"
576 "precision mediump float;\n"
577 "out vec4 col;\n"
578 "void main()\n"
579 "{\n"
580 " col = vec4(1,0,0,0);\n"
581 "}\n";
582
583 createFBO(16, 1, 4);
584 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
585 glUseProgram(program);
586
587 drawQuad(program, "vPosition", 0.0f, 1.0f, true);
588 ASSERT_GL_NO_ERROR();
589
590 for (int i = 0; i < 4; ++i)
591 {
592 for (int j = 0; j < 4; ++j)
593 {
594 const int arrayIndex = i * 4 + j;
595 if (i == j)
596 {
597 EXPECT_PIXEL_EQ(arrayIndex, 0, 255, 0, 0, 0);
598 }
599 else
600 {
601 EXPECT_PIXEL_EQ(arrayIndex, 0, 0, 0, 0, 0);
602 }
603 }
604 }
605 EXPECT_GL_NO_ERROR();
606}
607
608// The test checks that glDrawArraysInstanced can be used to render into two views.
609TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced)
610{
611 if (!requestMultiviewExtension())
612 {
613 return;
614 }
615
616 const std::string vsSource =
617 "#version 300 es\n"
618 "#extension GL_OVR_multiview : require\n"
619 "layout(num_views = 2) in;\n"
620 "in vec4 vPosition;\n"
621 "void main()\n"
622 "{\n"
623 " vec4 p = vPosition;\n"
624 " if (gl_InstanceID == 1){\n"
625 " p.y = .5*p.y + .5;\n"
626 " } else {\n"
627 " p.y = p.y*.5;\n"
628 " }\n"
629 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x*0.5 + 0.5 : p.x*0.5);\n"
630 " gl_Position.yzw = p.yzw;\n"
631 "}\n";
632
633 const std::string fsSource =
634 "#version 300 es\n"
635 "#extension GL_OVR_multiview : require\n"
636 "precision mediump float;\n"
637 "out vec4 col;\n"
638 "void main()\n"
639 "{\n"
640 " col = vec4(1,0,0,0);\n"
641 "}\n";
642
643 createFBO(4, 2, 2);
644 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
645 glUseProgram(program);
646
647 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 2);
648 ASSERT_GL_NO_ERROR();
649
650 const GLubyte expectedResult[4][8] = {
651 {0, 255, 255, 0}, {0, 255, 255, 0},
652 };
653 for (int row = 0; row < 2; ++row)
654 {
655 for (int col = 0; col < 4; ++col)
656 {
657 EXPECT_PIXEL_EQ(col, row, expectedResult[row][col], 0, 0, 0);
658 }
659 }
660}
661
Martin Radev553590a2017-07-31 16:40:39 +0300662// The test verifies that the attribute divisor is correctly adjusted when drawing with a multi-view
663// program. The test draws 4 instances of a quad each of which covers a single pixel. The x and y
664// offset of each quad are passed as separate attributes which are indexed based on the
665// corresponding attribute divisors. A divisor of 1 is used for the y offset to have all quads
666// drawn vertically next to each other. A divisor of 3 is used for the x offset to have the last
667// quad offsetted by one pixel to the right. Note that the number of views is divisible by 1, but
668// not by 3.
669TEST_P(MultiviewSideBySideRenderTest, AttribDivisor)
670{
671 if (!requestMultiviewExtension())
672 {
673 return;
674 }
675
676 const std::string &vsSource =
677 "#version 300 es\n"
678 "#extension GL_OVR_multiview2 : require\n"
679 "layout(num_views = 2) in;\n"
680 "in vec3 vPosition;\n"
681 "in float offsetX;\n"
682 "in float offsetY;\n"
683 "void main()\n"
684 "{\n"
685 " vec4 p = vec4(vPosition, 1.);\n"
686 " p.xy = p.xy * 0.25 - 0.75 + vec2(offsetX, offsetY);\n"
687 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x : p.x + 1.0);\n"
688 " gl_Position.yzw = p.yzw;\n"
689 "}\n";
690
691 const std::string &fsSource =
692 "#version 300 es\n"
693 "#extension GL_OVR_multiview2 : require\n"
694 "precision mediump float;\n"
695 "out vec4 col;\n"
696 "void main()\n"
697 "{\n"
698 " col = vec4(1,0,0,0);\n"
699 "}\n";
700 createFBO(8, 4, 2);
701 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
702 glUseProgram(program);
703
704 GLBuffer xOffsetVBO;
705 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
706 const GLfloat xOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.0f};
707 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, xOffsetData, GL_STATIC_DRAW);
708 GLint xOffsetLoc = glGetAttribLocation(program, "offsetX");
709 glVertexAttribPointer(xOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
710 glVertexAttribDivisor(xOffsetLoc, 3);
711 glEnableVertexAttribArray(xOffsetLoc);
712
713 GLBuffer yOffsetVBO;
714 glBindBuffer(GL_ARRAY_BUFFER, yOffsetVBO);
715 const GLfloat yOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.5f};
716 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, yOffsetData, GL_STATIC_DRAW);
717 GLint yOffsetLoc = glGetAttribLocation(program, "offsetY");
718 glVertexAttribDivisor(yOffsetLoc, 1);
719 glVertexAttribPointer(yOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
720 glEnableVertexAttribArray(yOffsetLoc);
721
722 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 4);
723 ASSERT_GL_NO_ERROR();
724
725 const GLubyte expectedRedChannel[4][8] = {{255, 0, 0, 0, 0, 0, 255, 0},
726 {255, 0, 0, 0, 0, 0, 255, 0},
727 {255, 0, 0, 0, 0, 0, 255, 0},
728 {0, 255, 0, 0, 0, 0, 0, 255}};
729 for (int row = 0; row < 4; ++row)
730 {
731 for (int col = 0; col < 8; ++col)
732 {
733 EXPECT_PIXEL_EQ(col, row, expectedRedChannel[row][col], 0, 0, 0);
734 }
735 }
736}
737
738// Test that different sequences of vertexAttribDivisor, useProgram and bindVertexArray in a
739// multi-view context propagate the correct divisor to the driver.
740TEST_P(MultiviewSideBySideRenderTest, DivisorOrderOfOperation)
741{
742 if (!requestMultiviewExtension())
743 {
744 return;
745 }
746
747 createFBO(2, 1, 2);
748
749 // Create multiview program.
750 const std::string &vs =
751 "#version 300 es\n"
752 "#extension GL_OVR_multiview2 : require\n"
753 "layout(num_views = 2) in;\n"
754 "layout(location = 0) in vec2 vPosition;\n"
755 "layout(location = 1) in float offsetX;\n"
756 "void main()\n"
757 "{\n"
758 " vec4 p = vec4(vPosition, 0.0, 1.0);\n"
759 " p.x += offsetX;\n"
760 " gl_Position = p;\n"
761 "}\n";
762
763 const std::string &fs =
764 "#version 300 es\n"
765 "#extension GL_OVR_multiview2 : require\n"
766 "precision mediump float;\n"
767 "out vec4 col;\n"
768 "void main()\n"
769 "{\n"
770 " col = vec4(1,0,0,0);\n"
771 "}\n";
772
773 ANGLE_GL_PROGRAM(program, vs, fs);
774
775 const std::string &dummyVS =
776 "#version 300 es\n"
777 "layout(location = 0) in vec2 vPosition;\n"
778 "layout(location = 1) in float offsetX;\n"
779 "void main()\n"
780 "{\n"
781 " gl_Position = vec4(vPosition, 0.0, 1.0);\n"
782 "}\n";
783
784 const std::string &dummyFS =
785 "#version 300 es\n"
786 "precision mediump float;\n"
787 "out vec4 col;\n"
788 "void main()\n"
789 "{\n"
790 " col = vec4(0,0,0,0);\n"
791 "}\n";
792
793 ANGLE_GL_PROGRAM(dummyProgram, dummyVS, dummyFS);
794
795 GLBuffer xOffsetVBO;
796 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
797 const GLfloat xOffsetData[12] = {0.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f,
798 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f};
799 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 12, xOffsetData, GL_STATIC_DRAW);
800
801 GLBuffer vertexVBO;
802 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
803 Vector2 kQuadVertices[6] = {Vector2(-1.f, -1.f), Vector2(1.f, -1.f), Vector2(1.f, 1.f),
804 Vector2(-1.f, -1.f), Vector2(1.f, 1.f), Vector2(-1.f, 1.f)};
805 glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
806
807 GLVertexArray vao[2];
808 for (size_t i = 0u; i < 2u; ++i)
809 {
810 glBindVertexArray(vao[i]);
811
812 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
813 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
814 glEnableVertexAttribArray(0);
815
816 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
817 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
818 glEnableVertexAttribArray(1);
819 }
820 ASSERT_GL_NO_ERROR();
821
822 glViewport(0, 0, 1, 1);
823 glScissor(0, 0, 1, 1);
Martin Radeveef80e42017-08-11 14:44:57 +0300824 glEnable(GL_SCISSOR_TEST);
Martin Radev553590a2017-07-31 16:40:39 +0300825 glClearColor(0, 0, 0, 0);
826
827 // Clear the buffers, propagate divisor to the driver, bind the vao and keep it active.
828 // It is necessary to call draw, so that the divisor is propagated and to guarantee that dirty
829 // bits are cleared.
830 glUseProgram(dummyProgram);
831 glBindVertexArray(vao[0]);
832 glVertexAttribDivisor(1, 0);
833 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
834 glUseProgram(0);
835 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
836 ASSERT_GL_NO_ERROR();
837
838 // Check that vertexAttribDivisor uses the number of views to update the divisor.
839 glUseProgram(program);
840 glVertexAttribDivisor(1, 1);
841 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
842 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
843 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
844
845 // Clear the buffers and propagate divisor to the driver.
846 // We keep the vao active and propagate the divisor to guarantee that there are no unresolved
847 // dirty bits when useProgram is called.
848 glUseProgram(dummyProgram);
849 glVertexAttribDivisor(1, 1);
850 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
851 glUseProgram(0);
852 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
853 ASSERT_GL_NO_ERROR();
854
855 // Check that useProgram uses the number of views to update the divisor.
856 glUseProgram(program);
857 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
858 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
859 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
860
861 // We go through similar steps as before.
862 glUseProgram(dummyProgram);
863 glVertexAttribDivisor(1, 1);
864 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
865 glUseProgram(0);
866 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
867 ASSERT_GL_NO_ERROR();
868
869 // Check that bindVertexArray uses the number of views to update the divisor.
870 {
871 // Call useProgram with vao[1] being active to guarantee that useProgram will adjust the
872 // divisor for vao[1] only.
873 glBindVertexArray(vao[1]);
874 glUseProgram(program);
875 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
876 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
877 glBindVertexArray(0);
878 ASSERT_GL_NO_ERROR();
879 }
880 // Bind vao[0] after useProgram is called to ensure that bindVertexArray is the call which
881 // adjusts the divisor.
882 glBindVertexArray(vao[0]);
883 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
884 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
885 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
886}
887
Martin Radev0d671c92017-08-10 16:41:52 +0300888// Test that no fragments pass the occlusion query for a multi-view vertex shader which always
889// transforms geometry to be outside of the clip region.
890TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryNothingVisible)
891{
892 if (!requestMultiviewExtension())
893 {
894 return;
895 }
896
897 const std::string vsSource =
898 "#version 300 es\n"
899 "#extension GL_OVR_multiview : require\n"
900 "layout(num_views = 2) in;\n"
901 "in vec3 vPosition;\n"
902 "void main()\n"
903 "{\n"
904 " gl_Position.x = 2.0;\n"
905 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
906 "}\n";
907
908 const std::string fsSource =
909 "#version 300 es\n"
910 "#extension GL_OVR_multiview : require\n"
911 "precision mediump float;\n"
912 "out vec4 col;\n"
913 "void main()\n"
914 "{\n"
915 " col = vec4(1,0,0,0);\n"
916 "}\n";
917 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
918 glUseProgram(program);
919 createFBO(2, 1, 2);
920
921 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
922 ASSERT_GL_NO_ERROR();
923 EXPECT_GL_FALSE(result);
924}
925
926// Test that there are fragments passing the occlusion query if only view 0 can produce
927// output.
928TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryOnlyLeftVisible)
929{
930 if (!requestMultiviewExtension())
931 {
932 return;
933 }
934
935 const std::string vsSource =
936 "#version 300 es\n"
937 "#extension GL_OVR_multiview : require\n"
938 "layout(num_views = 2) in;\n"
939 "in vec3 vPosition;\n"
940 "void main()\n"
941 "{\n"
942 " gl_Position.x = gl_ViewID_OVR == 0u ? vPosition.x : 2.0;\n"
943 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
944 "}\n";
945
946 const std::string fsSource =
947 "#version 300 es\n"
948 "#extension GL_OVR_multiview : require\n"
949 "precision mediump float;\n"
950 "out vec4 col;\n"
951 "void main()\n"
952 "{\n"
953 " col = vec4(1,0,0,0);\n"
954 "}\n";
955 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
956 glUseProgram(program);
957 createFBO(2, 1, 2);
958
959 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
960 ASSERT_GL_NO_ERROR();
961 EXPECT_GL_TRUE(result);
962}
963
964// Test that there are fragments passing the occlusion query if only view 1 can produce
965// output.
966TEST_P(MultiviewSideBySideOcclusionQueryTest, OcclusionQueryOnlyRightVisible)
967{
968 if (!requestMultiviewExtension())
969 {
970 return;
971 }
972
973 const std::string vsSource =
974 "#version 300 es\n"
975 "#extension GL_OVR_multiview : require\n"
976 "layout(num_views = 2) in;\n"
977 "in vec3 vPosition;\n"
978 "void main()\n"
979 "{\n"
980 " gl_Position.x = gl_ViewID_OVR == 1u ? vPosition.x : 2.0;\n"
981 " gl_Position.yzw = vec3(vPosition.yz, 1.);\n"
982 "}\n";
983
984 const std::string fsSource =
985 "#version 300 es\n"
986 "#extension GL_OVR_multiview : require\n"
987 "precision mediump float;\n"
988 "out vec4 col;\n"
989 "void main()\n"
990 "{\n"
991 " col = vec4(1,0,0,0);\n"
992 "}\n";
993 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
994 glUseProgram(program);
995 createFBO(2, 1, 2);
996
997 GLuint result = drawAndRetrieveOcclusionQueryResult(program);
998 ASSERT_GL_NO_ERROR();
999 EXPECT_GL_TRUE(result);
1000}
1001
Martin Radev41ac68e2017-06-06 12:16:58 +03001002// Test that a simple multi-view program which doesn't use gl_ViewID_OVR in neither VS nor FS
1003// compiles and links without an error.
1004TEST_P(MultiviewProgramGenerationTest, SimpleProgram)
1005{
1006 if (!requestMultiviewExtension())
1007 {
1008 return;
1009 }
1010
1011 const std::string vsSource =
1012 "#version 300 es\n"
1013 "#extension GL_OVR_multiview : require\n"
1014 "layout(num_views = 2) in;\n"
1015 "void main()\n"
1016 "{\n"
1017 "}\n";
1018
1019 const std::string fsSource =
1020 "#version 300 es\n"
1021 "#extension GL_OVR_multiview : require\n"
1022 "precision mediump float;\n"
1023 "void main()\n"
1024 "{\n"
1025 "}\n";
1026
1027 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
1028 glUseProgram(program);
1029
1030 EXPECT_GL_NO_ERROR();
1031}
1032
1033// Test that a simple multi-view program which uses gl_ViewID_OVR only in VS compiles and links
1034// without an error.
1035TEST_P(MultiviewProgramGenerationTest, UseViewIDInVertexShader)
1036{
1037 if (!requestMultiviewExtension())
1038 {
1039 return;
1040 }
1041
1042 const std::string vsSource =
1043 "#version 300 es\n"
1044 "#extension GL_OVR_multiview2 : require\n"
1045 "layout(num_views = 2) in;\n"
1046 "void main()\n"
1047 "{\n"
1048 " if (gl_ViewID_OVR == 0u) {\n"
1049 " gl_Position = vec4(1,0,0,1);\n"
1050 " } else {\n"
1051 " gl_Position = vec4(-1,0,0,1);\n"
1052 " }\n"
1053 "}\n";
1054
1055 const std::string fsSource =
1056 "#version 300 es\n"
1057 "#extension GL_OVR_multiview2 : require\n"
1058 "precision mediump float;\n"
1059 "void main()\n"
1060 "{\n"
1061 "}\n";
1062
1063 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
1064 glUseProgram(program);
1065
1066 EXPECT_GL_NO_ERROR();
1067}
1068
1069// Test that a simple multi-view program which uses gl_ViewID_OVR only in FS compiles and links
1070// without an error.
1071TEST_P(MultiviewProgramGenerationTest, UseViewIDInFragmentShader)
1072{
1073 if (!requestMultiviewExtension())
1074 {
1075 return;
1076 }
1077
1078 const std::string vsSource =
1079 "#version 300 es\n"
1080 "#extension GL_OVR_multiview2 : require\n"
1081 "layout(num_views = 2) in;\n"
1082 "void main()\n"
1083 "{\n"
1084 "}\n";
1085
1086 const std::string fsSource =
1087 "#version 300 es\n"
1088 "#extension GL_OVR_multiview2 : require\n"
1089 "precision mediump float;\n"
1090 "out vec4 col;\n"
1091 "void main()\n"
1092 "{\n"
1093 " if (gl_ViewID_OVR == 0u) {\n"
1094 " col = vec4(1,0,0,1);\n"
1095 " } else {\n"
1096 " col = vec4(-1,0,0,1);\n"
1097 " }\n"
1098 "}\n";
1099
1100 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
1101 glUseProgram(program);
1102
1103 EXPECT_GL_NO_ERROR();
1104}
1105
Martin Radev8f276e22017-05-30 12:05:52 +03001106ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
1107ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
Martin Radev0d671c92017-08-10 16:41:52 +03001108ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
1109ANGLE_INSTANTIATE_TEST(MultiviewSideBySideOcclusionQueryTest, ES3_OPENGL());
Martin Radev41ac68e2017-06-06 12:16:58 +03001110ANGLE_INSTANTIATE_TEST(MultiviewProgramGenerationTest, ES3_OPENGL(), ES3_D3D11());