1 //
2 // Copyright 2015 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 
7 #include "test_utils/ANGLETest.h"
8 #include "test_utils/gl_raii.h"
9 
10 using namespace angle;
11 
12 class DrawBuffersTest : public ANGLETest
13 {
14   protected:
DrawBuffersTest()15     DrawBuffersTest()
16     {
17         setWindowWidth(128);
18         setWindowHeight(128);
19         setConfigRedBits(8);
20         setConfigGreenBits(8);
21         setConfigBlueBits(8);
22         setConfigAlphaBits(8);
23         setConfigDepthBits(24);
24         mMaxDrawBuffers = 0;
25     }
26 
SetUp()27     virtual void SetUp()
28     {
29         ANGLETest::SetUp();
30 
31         // This test seems to fail on an nVidia machine when the window is hidden
32         SetWindowVisible(true);
33 
34         glGenFramebuffers(1, &mFBO);
35         glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
36 
37         glGenTextures(4, mTextures);
38 
39         for (size_t texIndex = 0; texIndex < ArraySize(mTextures); texIndex++)
40         {
41             glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]);
42             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
43                          GL_UNSIGNED_BYTE, nullptr);
44         }
45 
46         GLfloat data[] =
47         {
48             -1.0f, 1.0f,
49             -1.0f, -2.0f,
50             2.0f, 1.0f
51         };
52 
53         glGenBuffers(1, &mBuffer);
54         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
55         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6, data, GL_STATIC_DRAW);
56 
57         glGetIntegerv(GL_MAX_DRAW_BUFFERS, &mMaxDrawBuffers);
58 
59         ASSERT_GL_NO_ERROR();
60     }
61 
TearDown()62     virtual void TearDown()
63     {
64         glDeleteFramebuffers(1, &mFBO);
65         glDeleteTextures(4, mTextures);
66         glDeleteBuffers(1, &mBuffer);
67 
68         ANGLETest::TearDown();
69     }
70 
setupMRTProgramESSL3(bool bufferEnabled[8],GLuint * programOut)71     void setupMRTProgramESSL3(bool bufferEnabled[8], GLuint *programOut)
72     {
73         const std::string vertexShaderSource =
74             "#version 300 es\n"
75             "in vec4 position;\n"
76             "void main() {\n"
77             "    gl_Position = position;\n"
78             "}\n";
79 
80         std::stringstream strstr;
81 
82         strstr << "#version 300 es\n"
83                   "precision highp float;\n";
84 
85         for (unsigned int index = 0; index < 8; index++)
86         {
87             if (bufferEnabled[index])
88             {
89                 strstr << "layout(location = " << index << ") "
90                           "out vec4 value" << index << ";\n";
91             }
92         }
93 
94         strstr << "void main()\n"
95                   "{\n";
96 
97         for (unsigned int index = 0; index < 8; index++)
98         {
99             if (bufferEnabled[index])
100             {
101                 unsigned int r = (index + 1) & 1;
102                 unsigned int g = (index + 1) & 2;
103                 unsigned int b = (index + 1) & 4;
104 
105                 strstr << "    value" << index << " = vec4("
106                        << r << ".0, " << g << ".0, "
107                        << b << ".0, 1.0);\n";
108             }
109         }
110 
111         strstr << "}\n";
112 
113         *programOut = CompileProgram(vertexShaderSource, strstr.str());
114         if (*programOut == 0)
115         {
116             FAIL() << "shader compilation failed.";
117         }
118 
119         glUseProgram(*programOut);
120 
121         GLint location = glGetAttribLocation(*programOut, "position");
122         ASSERT_NE(location, -1);
123         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
124         glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 8, NULL);
125         glEnableVertexAttribArray(location);
126     }
127 
setupMRTProgramESSL1(bool bufferEnabled[8],GLuint * programOut)128     void setupMRTProgramESSL1(bool bufferEnabled[8], GLuint *programOut)
129     {
130         const std::string vertexShaderSource =
131             "attribute vec4 position;\n"
132             "void main() {\n"
133             "    gl_Position = position;\n"
134             "}\n";
135 
136         std::stringstream strstr;
137 
138         strstr << "#extension GL_EXT_draw_buffers : enable\n"
139                   "precision highp float;\n"
140                   "void main()\n"
141                   "{\n";
142 
143         for (unsigned int index = 0; index < 8; index++)
144         {
145             if (bufferEnabled[index])
146             {
147                 unsigned int r = (index + 1) & 1;
148                 unsigned int g = (index + 1) & 2;
149                 unsigned int b = (index + 1) & 4;
150 
151                 strstr << "    gl_FragData[" << index << "] = vec4("
152                     << r << ".0, " << g << ".0, "
153                     << b << ".0, 1.0);\n";
154             }
155         }
156 
157         strstr << "}\n";
158 
159         *programOut = CompileProgram(vertexShaderSource, strstr.str());
160         if (*programOut == 0)
161         {
162             FAIL() << "shader compilation failed.";
163         }
164 
165         glUseProgram(*programOut);
166 
167         GLint location = glGetAttribLocation(*programOut, "position");
168         ASSERT_NE(location, -1);
169         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
170         glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 8, NULL);
171         glEnableVertexAttribArray(location);
172     }
173 
setupMRTProgram(bool bufferEnabled[8],GLuint * programOut)174     void setupMRTProgram(bool bufferEnabled[8], GLuint *programOut)
175     {
176         if (getClientMajorVersion() == 3)
177         {
178             setupMRTProgramESSL3(bufferEnabled, programOut);
179         }
180         else
181         {
182             ASSERT_EQ(getClientMajorVersion(), 2);
183             setupMRTProgramESSL1(bufferEnabled, programOut);
184         }
185     }
186 
getColorForIndex(unsigned int index)187     static GLColor getColorForIndex(unsigned int index)
188     {
189         GLubyte r = (((index + 1) & 1) > 0) ? 255 : 0;
190         GLubyte g = (((index + 1) & 2) > 0) ? 255 : 0;
191         GLubyte b = (((index + 1) & 4) > 0) ? 255 : 0;
192         return GLColor(r, g, b, 255u);
193     }
194 
verifyAttachment2D(unsigned int index,GLuint textureName,GLenum target,GLint level)195     void verifyAttachment2D(unsigned int index, GLuint textureName, GLenum target, GLint level)
196     {
197         for (GLint colorAttachment = 0; colorAttachment < mMaxDrawBuffers; colorAttachment++)
198         {
199             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + colorAttachment, GL_TEXTURE_2D, 0, 0);
200         }
201 
202         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, textureName, level);
203 
204         EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, getColorForIndex(index));
205     }
206 
verifyAttachmentLayer(unsigned int index,GLuint texture,GLint level,GLint layer)207     void verifyAttachmentLayer(unsigned int index, GLuint texture, GLint level, GLint layer)
208     {
209         for (GLint colorAttachment = 0; colorAttachment < mMaxDrawBuffers; colorAttachment++)
210         {
211             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + colorAttachment,
212                                    GL_TEXTURE_2D, 0, 0);
213         }
214 
215         glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, level, layer);
216 
217         EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, getColorForIndex(index));
218     }
219 
220     GLuint mFBO;
221     GLuint mTextures[4];
222     GLuint mBuffer;
223     GLint mMaxDrawBuffers;
224 };
225 
226 // Verify that GL_MAX_DRAW_BUFFERS returns the expected values for D3D11
TEST_P(DrawBuffersTest,VerifyD3DLimits)227 TEST_P(DrawBuffersTest, VerifyD3DLimits)
228 {
229     EGLPlatformParameters platform = GetParam().eglParameters;
230     if (platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
231     {
232         if (platform.majorVersion == 9 && platform.minorVersion == 3)
233         {
234             // D3D11 Feature Level 9_3 supports 4 draw buffers
235             ASSERT_EQ(mMaxDrawBuffers, 4);
236         }
237         else
238         {
239             // D3D11 Feature Level 10_0+ supports 8 draw buffers
240             ASSERT_EQ(mMaxDrawBuffers, 8);
241         }
242     }
243     else
244     {
245         std::cout << "Test skipped for non-D3D11 renderers." << std::endl;
246         return;
247     }
248 }
249 
TEST_P(DrawBuffersTest,Gaps)250 TEST_P(DrawBuffersTest, Gaps)
251 {
252     if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers"))
253     {
254         std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available."
255                   << std::endl;
256         return;
257     }
258 
259     if (IsWindows() && IsAMD() && IsDesktopOpenGL())
260     {
261         // TODO(ynovikov): Investigate the failure (http://anglebug.com/1535)
262         std::cout << "Test disabled on Windows AMD OpenGL." << std::endl;
263         return;
264     }
265 
266     glBindTexture(GL_TEXTURE_2D, mTextures[0]);
267     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[0], 0);
268 
269     bool flags[8] = { false, true };
270 
271     GLuint program;
272     setupMRTProgram(flags, &program);
273 
274     const GLenum bufs[] =
275     {
276         GL_NONE,
277         GL_COLOR_ATTACHMENT1
278     };
279     glUseProgram(program);
280     glDrawBuffersEXT(2, bufs);
281     glDrawArrays(GL_TRIANGLES, 0, 3);
282 
283     verifyAttachment2D(1, mTextures[0], GL_TEXTURE_2D, 0);
284 
285     glDeleteProgram(program);
286 }
287 
TEST_P(DrawBuffersTest,FirstAndLast)288 TEST_P(DrawBuffersTest, FirstAndLast)
289 {
290     if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers"))
291     {
292         std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available."
293                   << std::endl;
294         return;
295     }
296 
297     if (IsWindows() && IsAMD() && IsDesktopOpenGL())
298     {
299         // TODO(ynovikov): Investigate the failure (https://anglebug.com/1533)
300         std::cout << "Test disabled on Windows AMD OpenGL." << std::endl;
301         return;
302     }
303 
304     glBindTexture(GL_TEXTURE_2D, mTextures[0]);
305     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
306 
307     glBindTexture(GL_TEXTURE_2D, mTextures[1]);
308     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, mTextures[1], 0);
309 
310     bool flags[8] = { true, false, false, true };
311 
312     GLuint program;
313     setupMRTProgram(flags, &program);
314 
315     const GLenum bufs[] =
316     {
317         GL_COLOR_ATTACHMENT0,
318         GL_NONE,
319         GL_NONE,
320         GL_COLOR_ATTACHMENT3
321     };
322 
323     glUseProgram(program);
324     glDrawBuffersEXT(4, bufs);
325     glDrawArrays(GL_TRIANGLES, 0, 3);
326 
327     verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0);
328     verifyAttachment2D(3, mTextures[1], GL_TEXTURE_2D, 0);
329 
330     EXPECT_GL_NO_ERROR();
331 
332     glDeleteProgram(program);
333 }
334 
TEST_P(DrawBuffersTest,FirstHalfNULL)335 TEST_P(DrawBuffersTest, FirstHalfNULL)
336 {
337     if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers"))
338     {
339         std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available."
340                   << std::endl;
341         return;
342     }
343 
344     if (IsWindows() && IsAMD() && IsDesktopOpenGL())
345     {
346         // TODO(ynovikov): Investigate the failure (https://anglebug.com/1533)
347         std::cout << "Test disabled on Windows AMD OpenGL." << std::endl;
348         return;
349     }
350 
351     bool flags[8] = { false };
352     GLenum bufs[8] = { GL_NONE };
353 
354     GLuint halfMaxDrawBuffers = static_cast<GLuint>(mMaxDrawBuffers) / 2;
355 
356     for (GLuint texIndex = 0; texIndex < halfMaxDrawBuffers; texIndex++)
357     {
358         glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]);
359         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + halfMaxDrawBuffers + texIndex, GL_TEXTURE_2D, mTextures[texIndex], 0);
360         flags[texIndex + halfMaxDrawBuffers] = true;
361         bufs[texIndex + halfMaxDrawBuffers] = GL_COLOR_ATTACHMENT0 + halfMaxDrawBuffers + texIndex;
362     }
363 
364     GLuint program;
365     setupMRTProgram(flags, &program);
366 
367     glUseProgram(program);
368     glDrawBuffersEXT(mMaxDrawBuffers, bufs);
369     glDrawArrays(GL_TRIANGLES, 0, 3);
370 
371     for (GLuint texIndex = 0; texIndex < halfMaxDrawBuffers; texIndex++)
372     {
373         verifyAttachment2D(texIndex + halfMaxDrawBuffers, mTextures[texIndex], GL_TEXTURE_2D, 0);
374     }
375 
376     EXPECT_GL_NO_ERROR();
377 
378     glDeleteProgram(program);
379 }
380 
TEST_P(DrawBuffersTest,UnwrittenOutputVariablesShouldNotCrash)381 TEST_P(DrawBuffersTest, UnwrittenOutputVariablesShouldNotCrash)
382 {
383     if (getClientMajorVersion() < 3 && !extensionEnabled("GL_EXT_draw_buffers"))
384     {
385         std::cout << "Test skipped because ES3 or GL_EXT_draw_buffers is not available."
386                   << std::endl;
387         return;
388     }
389 
390     // Bind two render targets but use a shader which writes only to the first one.
391     glBindTexture(GL_TEXTURE_2D, mTextures[0]);
392     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
393 
394     glBindTexture(GL_TEXTURE_2D, mTextures[1]);
395     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0);
396 
397     bool flags[8] = { true, false };
398 
399     GLuint program;
400     setupMRTProgram(flags, &program);
401 
402     const GLenum bufs[] =
403     {
404         GL_COLOR_ATTACHMENT0,
405         GL_COLOR_ATTACHMENT1,
406         GL_NONE,
407         GL_NONE,
408     };
409 
410     glUseProgram(program);
411     glDrawBuffersEXT(4, bufs);
412 
413     // This call should not crash when we dynamically generate the HLSL code.
414     glDrawArrays(GL_TRIANGLES, 0, 3);
415 
416     verifyAttachment2D(0, mTextures[0], GL_TEXTURE_2D, 0);
417 
418     EXPECT_GL_NO_ERROR();
419 
420     glDeleteProgram(program);
421 }
422 
423 // Test that binding multiple layers of a 3D texture works correctly
424 TEST_P(DrawBuffersTest, 3DTextures)
425 {
426     if (getClientMajorVersion() < 3)
427     {
428         std::cout << "Test skipped because ES3 is not available." << std::endl;
429         return;
430     }
431 
432     GLTexture texture;
433     glBindTexture(GL_TEXTURE_3D, texture.get());
434     glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), getWindowWidth(),
435                  0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
436 
437     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
438     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture.get(), 0, 1);
439     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, texture.get(), 0, 2);
440     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, texture.get(), 0, 3);
441 
442     bool flags[8] = {true, true, true, true, false};
443 
444     GLuint program;
445     setupMRTProgram(flags, &program);
446 
447     const GLenum bufs[] = {
448         GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
449     };
450 
451     glUseProgram(program);
452     glDrawBuffersEXT(4, bufs);
453 
454     glDrawArrays(GL_TRIANGLES, 0, 3);
455 
456     verifyAttachmentLayer(0, texture.get(), 0, 0);
457     verifyAttachmentLayer(1, texture.get(), 0, 1);
458     verifyAttachmentLayer(2, texture.get(), 0, 2);
459     verifyAttachmentLayer(3, texture.get(), 0, 3);
460 
461     EXPECT_GL_NO_ERROR();
462 
463     glDeleteProgram(program);
464 }
465 
466 // Test that binding multiple layers of a 2D array texture works correctly
467 TEST_P(DrawBuffersTest, 2DArrayTextures)
468 {
469     if (getClientMajorVersion() < 3)
470     {
471         std::cout << "Test skipped because ES3 is not available." << std::endl;
472         return;
473     }
474 
475     GLTexture texture;
476     glBindTexture(GL_TEXTURE_2D_ARRAY, texture.get());
477     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, getWindowWidth(), getWindowHeight(),
478                  getWindowWidth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
479 
480     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
481     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture.get(), 0, 1);
482     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, texture.get(), 0, 2);
483     glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, texture.get(), 0, 3);
484 
485     bool flags[8] = {true, true, true, true, false};
486 
487     GLuint program;
488     setupMRTProgram(flags, &program);
489 
490     const GLenum bufs[] = {
491         GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
492     };
493 
494     glUseProgram(program);
495     glDrawBuffersEXT(4, bufs);
496 
497     glDrawArrays(GL_TRIANGLES, 0, 3);
498 
499     verifyAttachmentLayer(0, texture.get(), 0, 0);
500     verifyAttachmentLayer(1, texture.get(), 0, 1);
501     verifyAttachmentLayer(2, texture.get(), 0, 2);
502     verifyAttachmentLayer(3, texture.get(), 0, 3);
503 
504     EXPECT_GL_NO_ERROR();
505 
506     glDeleteProgram(program);
507 }
508 
509 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
510 ANGLE_INSTANTIATE_TEST(DrawBuffersTest,
511                        ES2_D3D11(),
512                        ES3_D3D11(),
513                        ES2_D3D11_FL9_3(),
514                        ES2_OPENGL(),
515                        ES3_OPENGL(),
516                        ES2_OPENGLES(),
517                        ES3_OPENGLES());
518