blob: bf62701abf1a2f45f33f5251468d92dfa15cf264 [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 Radev14a26ae2017-07-24 15:56:29 +0300221// The test verifies that glDraw*Indirect:
222// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
223// than 1.
224// 2) does not generate any error if the draw framebuffer has exactly 1 view.
Martin Radev7cf61662017-07-26 17:10:53 +0300225TEST_P(MultiviewDrawValidationTest, IndirectDraw)
Martin Radev14a26ae2017-07-24 15:56:29 +0300226{
227 if (!requestMultiviewExtension())
228 {
229 return;
230 }
231
Martin Radev14a26ae2017-07-24 15:56:29 +0300232 const GLint viewportOffsets[4] = {0, 0, 2, 0};
Martin Radev14a26ae2017-07-24 15:56:29 +0300233
234 const std::string fsSource =
235 "#version 300 es\n"
236 "#extension GL_OVR_multiview : require\n"
237 "precision mediump float;\n"
238 "void main()\n"
239 "{}\n";
240
Martin Radev14a26ae2017-07-24 15:56:29 +0300241 GLBuffer commandBuffer;
242 glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer);
243 const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u};
244 glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW);
245 ASSERT_GL_NO_ERROR();
246
Martin Radev14a26ae2017-07-24 15:56:29 +0300247 // Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views.
248 {
249 const std::string &vsSource =
250 "#version 300 es\n"
251 "#extension GL_OVR_multiview : require\n"
252 "layout(num_views = 2) in;\n"
253 "void main()\n"
254 "{}\n";
255 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
256 glUseProgram(program);
257
Martin Radev7cf61662017-07-26 17:10:53 +0300258 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
259 0, 2, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300260
261 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
262 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
263
264 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
265 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
266 }
267
268 // Check that no errors are generated if the number of views is 1.
269 {
270 const std::string &vsSource =
271 "#version 300 es\n"
272 "#extension GL_OVR_multiview : require\n"
273 "layout(num_views = 1) in;\n"
274 "void main()\n"
275 "{}\n";
276 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
277 glUseProgram(program);
278
Martin Radev7cf61662017-07-26 17:10:53 +0300279 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
280 0, 1, &viewportOffsets[0]);
Martin Radev14a26ae2017-07-24 15:56:29 +0300281
282 glDrawArraysIndirect(GL_TRIANGLES, nullptr);
283 EXPECT_GL_NO_ERROR();
284
285 glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr);
286 EXPECT_GL_NO_ERROR();
287 }
288}
289
Martin Radev7cf61662017-07-26 17:10:53 +0300290// The test verifies that glDraw*:
291// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer and
292// program differs.
293// 2) does not generate any error if the number of views is the same.
294// 3) does not generate any error if the program does not use the multiview extension.
295TEST_P(MultiviewDrawValidationTest, NumViewsMismatch)
296{
297 if (!requestMultiviewExtension())
298 {
299 return;
300 }
301
302 const GLint viewportOffsets[4] = {0, 0, 2, 0};
303
304 const std::string &vsSource =
305 "#version 300 es\n"
306 "#extension GL_OVR_multiview : require\n"
307 "layout(num_views = 2) in;\n"
308 "void main()\n"
309 "{}\n";
310 const std::string &fsSource =
311 "#version 300 es\n"
312 "#extension GL_OVR_multiview : require\n"
313 "precision mediump float;\n"
314 "void main()\n"
315 "{}\n";
316 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
317 glUseProgram(program);
318
319 // Check for a GL_INVALID_OPERATION error with the framebuffer and program having different
320 // number of views.
321 {
322 // The framebuffer has only 1 view.
323 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
324 0, 1, &viewportOffsets[0]);
325
326 glDrawArrays(GL_TRIANGLES, 0, 3);
327 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
328
329 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
330 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
331 }
332
333 // Check that no errors are generated if the number of views in both program and draw
334 // framebuffer matches.
335 {
336 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
337 0, 2, &viewportOffsets[0]);
338
339 glDrawArrays(GL_TRIANGLES, 0, 3);
340 EXPECT_GL_NO_ERROR();
341
342 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
343 EXPECT_GL_NO_ERROR();
344 }
345
346 // Check that no errors are generated if the program does not use the multiview extension.
347 {
348 const std::string &vsSourceNoMultiview =
349 "#version 300 es\n"
350 "void main()\n"
351 "{}\n";
352 const std::string &fsSourceNoMultiview =
353 "#version 300 es\n"
354 "precision mediump float;\n"
355 "void main()\n"
356 "{}\n";
357 ANGLE_GL_PROGRAM(programNoMultiview, vsSourceNoMultiview, fsSourceNoMultiview);
358 glUseProgram(programNoMultiview);
359
360 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
361 0, 2, &viewportOffsets[0]);
362
363 glDrawArrays(GL_TRIANGLES, 0, 3);
364 EXPECT_GL_NO_ERROR();
365
366 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
367 EXPECT_GL_NO_ERROR();
368 }
369}
370
Martin Radev7e69f762017-07-27 14:54:13 +0300371// The test verifies that glDraw*:
372// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
373// greater than 1 and there is an active transform feedback object.
374// 2) does not generate any error if the number of views in the draw framebuffer is 1.
375TEST_P(MultiviewDrawValidationTest, ActiveTransformFeedback)
376{
377 if (!requestMultiviewExtension())
378 {
379 return;
380 }
381
382 const GLint viewportOffsets[4] = {0, 0, 2, 0};
383
384 const std::string &vsSource =
385 "#version 300 es\n"
386 "void main()\n"
387 "{}\n";
388 const std::string &fsSource =
389 "#version 300 es\n"
390 "precision mediump float;\n"
391 "void main()\n"
392 "{}\n";
393 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
394 glUseProgram(program);
395
396 GLBuffer tbo;
397 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);
398 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 4u, nullptr, GL_STATIC_DRAW);
399
400 GLTransformFeedback transformFeedback;
401 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedback);
402 glBeginTransformFeedback(GL_TRIANGLES);
403 ASSERT_GL_NO_ERROR();
404
405 // Check that drawArrays generates an error when there is an active transform feedback object
406 // and the number of views in the draw framebuffer is greater than 1.
407 {
408 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
409 0, 2, &viewportOffsets[0]);
410 glDrawArrays(GL_TRIANGLES, 0, 3);
411 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
412 }
413
414 // Check that drawArrays does not generate an error when the number of views in the draw
415 // framebuffer is 1.
416 {
417 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
418 0, 1, &viewportOffsets[0]);
419 glDrawArrays(GL_TRIANGLES, 0, 3);
420 EXPECT_GL_NO_ERROR();
421 }
422
423 glEndTransformFeedback();
424}
425
Martin Radevffe754b2017-07-31 10:38:07 +0300426// The test verifies that glDraw*:
427// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is
428// greater than 1 and there is an active query for target GL_TIME_ELAPSED_EXT.
429// 2) does not generate any error if the number of views in the draw framebuffer is 1.
430TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery)
431{
432 if (!requestMultiviewExtension())
433 {
434 return;
435 }
436
437 if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
438 {
439 std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
440 << std::endl;
441 return;
442 }
443
444 const GLint viewportOffsets[4] = {0, 0, 2, 0};
445 const std::string &vsSource =
446 "#version 300 es\n"
447 "void main()\n"
448 "{}\n";
449 const std::string &fsSource =
450 "#version 300 es\n"
451 "precision mediump float;\n"
452 "void main()\n"
453 "{}\n";
454 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
455 glUseProgram(program);
456
457 GLuint query = 0u;
458 glGenQueriesEXT(1, &query);
459 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
460
461 // Check first case.
462 {
463 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
464 0, 2, &viewportOffsets[0]);
465 glDrawArrays(GL_TRIANGLES, 0, 3);
466 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
467 }
468
469 // Check second case.
470 {
471 glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
472 0, 1, &viewportOffsets[0]);
473 glDrawArrays(GL_TRIANGLES, 0, 3);
474 EXPECT_GL_NO_ERROR();
475 }
476
477 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
478 glDeleteQueries(1, &query);
479}
480
Martin Radev8f276e22017-05-30 12:05:52 +0300481// The test checks that glDrawArrays can be used to render into two views.
482TEST_P(MultiviewSideBySideRenderDualViewTest, DrawArrays)
483{
484 if (!requestMultiviewExtension())
485 {
486 return;
487 }
488 drawQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
489 ASSERT_GL_NO_ERROR();
490
491 checkOutput();
492}
493
494// The test checks that glDrawElements can be used to render into two views.
495TEST_P(MultiviewSideBySideRenderDualViewTest, DrawElements)
496{
497 if (!requestMultiviewExtension())
498 {
499 return;
500 }
501 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true);
502 ASSERT_GL_NO_ERROR();
503
504 checkOutput();
505}
506
507// The test checks that glDrawRangeElements can be used to render into two views.
508TEST_P(MultiviewSideBySideRenderDualViewTest, DrawRangeElements)
509{
510 if (!requestMultiviewExtension())
511 {
512 return;
513 }
514 drawIndexedQuad(mProgram, "vPosition", 0.0f, 1.0f, true, true);
515 ASSERT_GL_NO_ERROR();
516
517 checkOutput();
518}
519
520// The test checks that glDrawArrays can be used to render into four views.
521TEST_P(MultiviewSideBySideRenderTest, DrawArraysFourViews)
522{
523 if (!requestMultiviewExtension())
524 {
525 return;
526 }
527
528 const std::string vsSource =
529 "#version 300 es\n"
530 "#extension GL_OVR_multiview2 : require\n"
531 "layout(num_views = 4) in;\n"
532 "in vec4 vPosition;\n"
533 "void main()\n"
534 "{\n"
535 " if (gl_ViewID_OVR == 0u) {\n"
536 " gl_Position.x = vPosition.x*0.25 - 0.75;\n"
537 " } else if (gl_ViewID_OVR == 1u) {\n"
538 " gl_Position.x = vPosition.x*0.25 - 0.25;\n"
539 " } else if (gl_ViewID_OVR == 2u) {\n"
540 " gl_Position.x = vPosition.x*0.25 + 0.25;\n"
541 " } else {\n"
542 " gl_Position.x = vPosition.x*0.25 + 0.75;\n"
543 " }"
544 " gl_Position.yzw = vPosition.yzw;\n"
545 "}\n";
546
547 const std::string fsSource =
548 "#version 300 es\n"
549 "#extension GL_OVR_multiview2 : require\n"
550 "precision mediump float;\n"
551 "out vec4 col;\n"
552 "void main()\n"
553 "{\n"
554 " col = vec4(1,0,0,0);\n"
555 "}\n";
556
557 createFBO(16, 1, 4);
558 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
559 glUseProgram(program);
560
561 drawQuad(program, "vPosition", 0.0f, 1.0f, true);
562 ASSERT_GL_NO_ERROR();
563
564 for (int i = 0; i < 4; ++i)
565 {
566 for (int j = 0; j < 4; ++j)
567 {
568 const int arrayIndex = i * 4 + j;
569 if (i == j)
570 {
571 EXPECT_PIXEL_EQ(arrayIndex, 0, 255, 0, 0, 0);
572 }
573 else
574 {
575 EXPECT_PIXEL_EQ(arrayIndex, 0, 0, 0, 0, 0);
576 }
577 }
578 }
579 EXPECT_GL_NO_ERROR();
580}
581
582// The test checks that glDrawArraysInstanced can be used to render into two views.
583TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced)
584{
585 if (!requestMultiviewExtension())
586 {
587 return;
588 }
589
590 const std::string vsSource =
591 "#version 300 es\n"
592 "#extension GL_OVR_multiview : require\n"
593 "layout(num_views = 2) in;\n"
594 "in vec4 vPosition;\n"
595 "void main()\n"
596 "{\n"
597 " vec4 p = vPosition;\n"
598 " if (gl_InstanceID == 1){\n"
599 " p.y = .5*p.y + .5;\n"
600 " } else {\n"
601 " p.y = p.y*.5;\n"
602 " }\n"
603 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x*0.5 + 0.5 : p.x*0.5);\n"
604 " gl_Position.yzw = p.yzw;\n"
605 "}\n";
606
607 const std::string fsSource =
608 "#version 300 es\n"
609 "#extension GL_OVR_multiview : require\n"
610 "precision mediump float;\n"
611 "out vec4 col;\n"
612 "void main()\n"
613 "{\n"
614 " col = vec4(1,0,0,0);\n"
615 "}\n";
616
617 createFBO(4, 2, 2);
618 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
619 glUseProgram(program);
620
621 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 2);
622 ASSERT_GL_NO_ERROR();
623
624 const GLubyte expectedResult[4][8] = {
625 {0, 255, 255, 0}, {0, 255, 255, 0},
626 };
627 for (int row = 0; row < 2; ++row)
628 {
629 for (int col = 0; col < 4; ++col)
630 {
631 EXPECT_PIXEL_EQ(col, row, expectedResult[row][col], 0, 0, 0);
632 }
633 }
634}
635
Martin Radev553590a2017-07-31 16:40:39 +0300636// The test verifies that the attribute divisor is correctly adjusted when drawing with a multi-view
637// program. The test draws 4 instances of a quad each of which covers a single pixel. The x and y
638// offset of each quad are passed as separate attributes which are indexed based on the
639// corresponding attribute divisors. A divisor of 1 is used for the y offset to have all quads
640// drawn vertically next to each other. A divisor of 3 is used for the x offset to have the last
641// quad offsetted by one pixel to the right. Note that the number of views is divisible by 1, but
642// not by 3.
643TEST_P(MultiviewSideBySideRenderTest, AttribDivisor)
644{
645 if (!requestMultiviewExtension())
646 {
647 return;
648 }
649
650 const std::string &vsSource =
651 "#version 300 es\n"
652 "#extension GL_OVR_multiview2 : require\n"
653 "layout(num_views = 2) in;\n"
654 "in vec3 vPosition;\n"
655 "in float offsetX;\n"
656 "in float offsetY;\n"
657 "void main()\n"
658 "{\n"
659 " vec4 p = vec4(vPosition, 1.);\n"
660 " p.xy = p.xy * 0.25 - 0.75 + vec2(offsetX, offsetY);\n"
661 " gl_Position.x = (gl_ViewID_OVR == 0u ? p.x : p.x + 1.0);\n"
662 " gl_Position.yzw = p.yzw;\n"
663 "}\n";
664
665 const std::string &fsSource =
666 "#version 300 es\n"
667 "#extension GL_OVR_multiview2 : require\n"
668 "precision mediump float;\n"
669 "out vec4 col;\n"
670 "void main()\n"
671 "{\n"
672 " col = vec4(1,0,0,0);\n"
673 "}\n";
674 createFBO(8, 4, 2);
675 ANGLE_GL_PROGRAM(program, vsSource, fsSource);
676 glUseProgram(program);
677
678 GLBuffer xOffsetVBO;
679 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
680 const GLfloat xOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.0f};
681 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, xOffsetData, GL_STATIC_DRAW);
682 GLint xOffsetLoc = glGetAttribLocation(program, "offsetX");
683 glVertexAttribPointer(xOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
684 glVertexAttribDivisor(xOffsetLoc, 3);
685 glEnableVertexAttribArray(xOffsetLoc);
686
687 GLBuffer yOffsetVBO;
688 glBindBuffer(GL_ARRAY_BUFFER, yOffsetVBO);
689 const GLfloat yOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.5f};
690 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, yOffsetData, GL_STATIC_DRAW);
691 GLint yOffsetLoc = glGetAttribLocation(program, "offsetY");
692 glVertexAttribDivisor(yOffsetLoc, 1);
693 glVertexAttribPointer(yOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
694 glEnableVertexAttribArray(yOffsetLoc);
695
696 drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 4);
697 ASSERT_GL_NO_ERROR();
698
699 const GLubyte expectedRedChannel[4][8] = {{255, 0, 0, 0, 0, 0, 255, 0},
700 {255, 0, 0, 0, 0, 0, 255, 0},
701 {255, 0, 0, 0, 0, 0, 255, 0},
702 {0, 255, 0, 0, 0, 0, 0, 255}};
703 for (int row = 0; row < 4; ++row)
704 {
705 for (int col = 0; col < 8; ++col)
706 {
707 EXPECT_PIXEL_EQ(col, row, expectedRedChannel[row][col], 0, 0, 0);
708 }
709 }
710}
711
712// Test that different sequences of vertexAttribDivisor, useProgram and bindVertexArray in a
713// multi-view context propagate the correct divisor to the driver.
714TEST_P(MultiviewSideBySideRenderTest, DivisorOrderOfOperation)
715{
716 if (!requestMultiviewExtension())
717 {
718 return;
719 }
720
721 createFBO(2, 1, 2);
722
723 // Create multiview program.
724 const std::string &vs =
725 "#version 300 es\n"
726 "#extension GL_OVR_multiview2 : require\n"
727 "layout(num_views = 2) in;\n"
728 "layout(location = 0) in vec2 vPosition;\n"
729 "layout(location = 1) in float offsetX;\n"
730 "void main()\n"
731 "{\n"
732 " vec4 p = vec4(vPosition, 0.0, 1.0);\n"
733 " p.x += offsetX;\n"
734 " gl_Position = p;\n"
735 "}\n";
736
737 const std::string &fs =
738 "#version 300 es\n"
739 "#extension GL_OVR_multiview2 : require\n"
740 "precision mediump float;\n"
741 "out vec4 col;\n"
742 "void main()\n"
743 "{\n"
744 " col = vec4(1,0,0,0);\n"
745 "}\n";
746
747 ANGLE_GL_PROGRAM(program, vs, fs);
748
749 const std::string &dummyVS =
750 "#version 300 es\n"
751 "layout(location = 0) in vec2 vPosition;\n"
752 "layout(location = 1) in float offsetX;\n"
753 "void main()\n"
754 "{\n"
755 " gl_Position = vec4(vPosition, 0.0, 1.0);\n"
756 "}\n";
757
758 const std::string &dummyFS =
759 "#version 300 es\n"
760 "precision mediump float;\n"
761 "out vec4 col;\n"
762 "void main()\n"
763 "{\n"
764 " col = vec4(0,0,0,0);\n"
765 "}\n";
766
767 ANGLE_GL_PROGRAM(dummyProgram, dummyVS, dummyFS);
768
769 GLBuffer xOffsetVBO;
770 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
771 const GLfloat xOffsetData[12] = {0.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f,
772 4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f};
773 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 12, xOffsetData, GL_STATIC_DRAW);
774
775 GLBuffer vertexVBO;
776 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
777 Vector2 kQuadVertices[6] = {Vector2(-1.f, -1.f), Vector2(1.f, -1.f), Vector2(1.f, 1.f),
778 Vector2(-1.f, -1.f), Vector2(1.f, 1.f), Vector2(-1.f, 1.f)};
779 glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
780
781 GLVertexArray vao[2];
782 for (size_t i = 0u; i < 2u; ++i)
783 {
784 glBindVertexArray(vao[i]);
785
786 glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
787 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
788 glEnableVertexAttribArray(0);
789
790 glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
791 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
792 glEnableVertexAttribArray(1);
793 }
794 ASSERT_GL_NO_ERROR();
795
796 glViewport(0, 0, 1, 1);
797 glScissor(0, 0, 1, 1);
798 glClearColor(0, 0, 0, 0);
799
800 // Clear the buffers, propagate divisor to the driver, bind the vao and keep it active.
801 // It is necessary to call draw, so that the divisor is propagated and to guarantee that dirty
802 // bits are cleared.
803 glUseProgram(dummyProgram);
804 glBindVertexArray(vao[0]);
805 glVertexAttribDivisor(1, 0);
806 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
807 glUseProgram(0);
808 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
809 ASSERT_GL_NO_ERROR();
810
811 // Check that vertexAttribDivisor uses the number of views to update the divisor.
812 glUseProgram(program);
813 glVertexAttribDivisor(1, 1);
814 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
815 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
816 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
817
818 // Clear the buffers and propagate divisor to the driver.
819 // We keep the vao active and propagate the divisor to guarantee that there are no unresolved
820 // dirty bits when useProgram is called.
821 glUseProgram(dummyProgram);
822 glVertexAttribDivisor(1, 1);
823 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
824 glUseProgram(0);
825 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
826 ASSERT_GL_NO_ERROR();
827
828 // Check that useProgram uses the number of views to update the divisor.
829 glUseProgram(program);
830 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
831 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
832 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
833
834 // We go through similar steps as before.
835 glUseProgram(dummyProgram);
836 glVertexAttribDivisor(1, 1);
837 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
838 glUseProgram(0);
839 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
840 ASSERT_GL_NO_ERROR();
841
842 // Check that bindVertexArray uses the number of views to update the divisor.
843 {
844 // Call useProgram with vao[1] being active to guarantee that useProgram will adjust the
845 // divisor for vao[1] only.
846 glBindVertexArray(vao[1]);
847 glUseProgram(program);
848 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
849 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
850 glBindVertexArray(0);
851 ASSERT_GL_NO_ERROR();
852 }
853 // Bind vao[0] after useProgram is called to ensure that bindVertexArray is the call which
854 // adjusts the divisor.
855 glBindVertexArray(vao[0]);
856 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
857 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
858 EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
859}
860
Martin Radev8f276e22017-05-30 12:05:52 +0300861ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
862ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
863ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());