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