1 //
2 // Copyright 2018 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 // BlendFuncExtendedTest
7 // Test EXT_blend_func_extended
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11
12 #include "util/shader_utils.h"
13
14 #include <algorithm>
15 #include <cmath>
16 #include <fstream>
17
18 using namespace angle;
19
20 namespace
21 {
22
23 // Partial implementation of weight function for GLES 2 blend equation that
24 // is dual-source aware.
25 template <int factor, int index>
Weight(const float[4],const float src[4],const float src1[4])26 float Weight(const float /*dst*/[4], const float src[4], const float src1[4])
27 {
28 if (factor == GL_SRC_COLOR)
29 return src[index];
30 if (factor == GL_SRC_ALPHA)
31 return src[3];
32 if (factor == GL_SRC1_COLOR_EXT)
33 return src1[index];
34 if (factor == GL_SRC1_ALPHA_EXT)
35 return src1[3];
36 if (factor == GL_ONE_MINUS_SRC1_COLOR_EXT)
37 return 1.0f - src1[index];
38 if (factor == GL_ONE_MINUS_SRC1_ALPHA_EXT)
39 return 1.0f - src1[3];
40 return 0.0f;
41 }
42
ScaleChannel(float weight)43 GLubyte ScaleChannel(float weight)
44 {
45 return static_cast<GLubyte>(std::floor(std::max(0.0f, std::min(1.0f, weight)) * 255.0f));
46 }
47
48 // Implementation of GLES 2 blend equation that is dual-source aware.
49 template <int RGBs, int RGBd, int As, int Ad>
BlendEquationFuncAdd(const float dst[4],const float src[4],const float src1[4],angle::GLColor * result)50 void BlendEquationFuncAdd(const float dst[4],
51 const float src[4],
52 const float src1[4],
53 angle::GLColor *result)
54 {
55 float r[4];
56 r[0] = src[0] * Weight<RGBs, 0>(dst, src, src1) + dst[0] * Weight<RGBd, 0>(dst, src, src1);
57 r[1] = src[1] * Weight<RGBs, 1>(dst, src, src1) + dst[1] * Weight<RGBd, 1>(dst, src, src1);
58 r[2] = src[2] * Weight<RGBs, 2>(dst, src, src1) + dst[2] * Weight<RGBd, 2>(dst, src, src1);
59 r[3] = src[3] * Weight<As, 3>(dst, src, src1) + dst[3] * Weight<Ad, 3>(dst, src, src1);
60
61 result->R = ScaleChannel(r[0]);
62 result->G = ScaleChannel(r[1]);
63 result->B = ScaleChannel(r[2]);
64 result->A = ScaleChannel(r[3]);
65 }
66
CheckPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLint tolerance,const angle::GLColor & color)67 void CheckPixels(GLint x,
68 GLint y,
69 GLsizei width,
70 GLsizei height,
71 GLint tolerance,
72 const angle::GLColor &color)
73 {
74 for (GLint yy = 0; yy < height; ++yy)
75 {
76 for (GLint xx = 0; xx < width; ++xx)
77 {
78 const auto px = x + xx;
79 const auto py = y + yy;
80 EXPECT_PIXEL_COLOR_NEAR(px, py, color, 1);
81 }
82 }
83 }
84
85 const GLuint kWidth = 100;
86 const GLuint kHeight = 100;
87
88 class EXTBlendFuncExtendedTest : public ANGLETest
89 {};
90
91 class EXTBlendFuncExtendedTestES3 : public ANGLETest
92 {};
93
94 class EXTBlendFuncExtendedDrawTest : public ANGLETest
95 {
96 protected:
EXTBlendFuncExtendedDrawTest()97 EXTBlendFuncExtendedDrawTest() : mProgram(0)
98 {
99 setWindowWidth(kWidth);
100 setWindowHeight(kHeight);
101 setConfigRedBits(8);
102 setConfigGreenBits(8);
103 setConfigBlueBits(8);
104 setConfigAlphaBits(8);
105 }
106
testSetUp()107 void testSetUp() override
108 {
109 glGenBuffers(1, &mVBO);
110 glBindBuffer(GL_ARRAY_BUFFER, mVBO);
111
112 static const float vertices[] = {
113 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
114 };
115 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
116
117 ASSERT_GL_NO_ERROR();
118 }
119
testTearDown()120 void testTearDown() override
121 {
122 glDeleteBuffers(1, &mVBO);
123 if (mProgram)
124 {
125 glDeleteProgram(mProgram);
126 }
127
128 ASSERT_GL_NO_ERROR();
129 }
130
makeProgram(const char * vertSource,const char * fragSource)131 void makeProgram(const char *vertSource, const char *fragSource)
132 {
133 mProgram = CompileProgram(vertSource, fragSource);
134
135 ASSERT_NE(0u, mProgram);
136 }
137
getVertexAttribLocation(const char * name)138 virtual GLint getVertexAttribLocation(const char *name)
139 {
140 return glGetAttribLocation(mProgram, name);
141 }
142
getFragmentUniformLocation(const char * name)143 virtual GLint getFragmentUniformLocation(const char *name)
144 {
145 return glGetUniformLocation(mProgram, name);
146 }
147
setUniform4f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3)148 virtual void setUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
149 {
150 glUniform4f(location, v0, v1, v2, v3);
151 }
152
drawTest()153 void drawTest()
154 {
155 glUseProgram(mProgram);
156
157 GLint position = getVertexAttribLocation(essl1_shaders::PositionAttrib());
158 GLint src0 = getFragmentUniformLocation("src0");
159 GLint src1 = getFragmentUniformLocation("src1");
160 ASSERT_GL_NO_ERROR();
161
162 glBindBuffer(GL_ARRAY_BUFFER, mVBO);
163 glEnableVertexAttribArray(position);
164 glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
165 ASSERT_GL_NO_ERROR();
166
167 static const float kDst[4] = {0.5f, 0.5f, 0.5f, 0.5f};
168 static const float kSrc0[4] = {1.0f, 1.0f, 1.0f, 1.0f};
169 static const float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f};
170
171 setUniform4f(src0, kSrc0[0], kSrc0[1], kSrc0[2], kSrc0[3]);
172 setUniform4f(src1, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]);
173 ASSERT_GL_NO_ERROR();
174
175 glEnable(GL_BLEND);
176 glBlendEquation(GL_FUNC_ADD);
177 glViewport(0, 0, kWidth, kHeight);
178 glClearColor(kDst[0], kDst[1], kDst[2], kDst[3]);
179 ASSERT_GL_NO_ERROR();
180
181 {
182 glBlendFuncSeparate(GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC1_COLOR_EXT,
183 GL_ONE_MINUS_SRC1_ALPHA_EXT);
184
185 glClear(GL_COLOR_BUFFER_BIT);
186 glDrawArrays(GL_TRIANGLES, 0, 6);
187 ASSERT_GL_NO_ERROR();
188
189 // verify
190 angle::GLColor color;
191 BlendEquationFuncAdd<GL_SRC1_COLOR_EXT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC1_COLOR_EXT,
192 GL_ONE_MINUS_SRC1_ALPHA_EXT>(kDst, kSrc0, kSrc1, &color);
193
194 CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, 1, color);
195 CheckPixels(kWidth - 1, 0, 1, 1, 1, color);
196 }
197
198 {
199 glBlendFuncSeparate(GL_ONE_MINUS_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC_ALPHA,
200 GL_ONE_MINUS_SRC_COLOR, GL_SRC1_ALPHA_EXT);
201
202 glClear(GL_COLOR_BUFFER_BIT);
203 glDrawArrays(GL_TRIANGLES, 0, 6);
204 ASSERT_GL_NO_ERROR();
205
206 // verify
207 angle::GLColor color;
208 BlendEquationFuncAdd<GL_ONE_MINUS_SRC1_COLOR_EXT, GL_ONE_MINUS_SRC_ALPHA,
209 GL_ONE_MINUS_SRC_COLOR, GL_SRC1_ALPHA_EXT>(kDst, kSrc0, kSrc1,
210 &color);
211
212 CheckPixels(kWidth / 4, (3 * kHeight) / 4, 1, 1, 1, color);
213 CheckPixels(kWidth - 1, 0, 1, 1, 1, color);
214 }
215 }
216
217 GLuint mVBO;
218 GLuint mProgram;
219 };
220
221 class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest
222 {
223 protected:
EXTBlendFuncExtendedDrawTestES3()224 EXTBlendFuncExtendedDrawTestES3() : EXTBlendFuncExtendedDrawTest(), mIsES31OrNewer(false) {}
225
testSetUp()226 void testSetUp() override
227 {
228 EXTBlendFuncExtendedDrawTest::testSetUp();
229 if (getClientMajorVersion() > 3 ||
230 (getClientMajorVersion() == 3 && getClientMinorVersion() >= 1))
231 {
232 mIsES31OrNewer = true;
233 }
234 }
235
checkOutputIndexQuery(const char * name,GLint expectedIndex)236 virtual void checkOutputIndexQuery(const char *name, GLint expectedIndex)
237 {
238 GLint index = glGetFragDataIndexEXT(mProgram, name);
239 EXPECT_EQ(expectedIndex, index);
240 if (mIsES31OrNewer)
241 {
242 index = glGetProgramResourceLocationIndexEXT(mProgram, GL_PROGRAM_OUTPUT, name);
243 EXPECT_EQ(expectedIndex, index);
244 }
245 else
246 {
247 glGetProgramResourceLocationIndexEXT(mProgram, GL_PROGRAM_OUTPUT, name);
248 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
249 }
250 }
251
LinkProgram()252 void LinkProgram()
253 {
254 glLinkProgram(mProgram);
255 GLint linked = 0;
256 glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
257 EXPECT_NE(0, linked);
258 glUseProgram(mProgram);
259 return;
260 }
261
262 private:
263 bool mIsES31OrNewer;
264 };
265
266 class EXTBlendFuncExtendedDrawTestES31 : public EXTBlendFuncExtendedDrawTestES3
267 {
268 protected:
EXTBlendFuncExtendedDrawTestES31()269 EXTBlendFuncExtendedDrawTestES31()
270 : EXTBlendFuncExtendedDrawTestES3(), mPipeline(0), mVertexProgram(0), mFragProgram(0)
271 {}
272
getVertexAttribLocation(const char * name)273 GLint getVertexAttribLocation(const char *name) override
274 {
275 return glGetAttribLocation(mVertexProgram, name);
276 }
277
getFragmentUniformLocation(const char * name)278 GLint getFragmentUniformLocation(const char *name) override
279 {
280 return glGetUniformLocation(mFragProgram, name);
281 }
282
setUniform4f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3)283 void setUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) override
284 {
285 glActiveShaderProgram(mPipeline, mFragProgram);
286 EXTBlendFuncExtendedDrawTest::setUniform4f(location, v0, v1, v2, v3);
287 }
288
checkOutputIndexQuery(const char * name,GLint expectedIndex)289 void checkOutputIndexQuery(const char *name, GLint expectedIndex) override
290 {
291 GLint index = glGetFragDataIndexEXT(mFragProgram, name);
292 EXPECT_EQ(expectedIndex, index);
293 index = glGetProgramResourceLocationIndexEXT(mFragProgram, GL_PROGRAM_OUTPUT, name);
294 EXPECT_EQ(expectedIndex, index);
295 }
296
setupProgramPipeline(const char * vertexSource,const char * fragmentSource)297 void setupProgramPipeline(const char *vertexSource, const char *fragmentSource)
298 {
299 mVertexProgram = createShaderProgram(GL_VERTEX_SHADER, vertexSource);
300 ASSERT_NE(mVertexProgram, 0u);
301 mFragProgram = createShaderProgram(GL_FRAGMENT_SHADER, fragmentSource);
302 ASSERT_NE(mFragProgram, 0u);
303
304 // Generate a program pipeline and attach the programs to their respective stages
305 glGenProgramPipelines(1, &mPipeline);
306 EXPECT_GL_NO_ERROR();
307 glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertexProgram);
308 EXPECT_GL_NO_ERROR();
309 glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProgram);
310 EXPECT_GL_NO_ERROR();
311 glBindProgramPipeline(mPipeline);
312 EXPECT_GL_NO_ERROR();
313 }
314
createShaderProgram(GLenum type,const GLchar * shaderString)315 GLuint createShaderProgram(GLenum type, const GLchar *shaderString)
316 {
317 GLShader shader(type);
318 if (!shader.get())
319 {
320 return 0;
321 }
322
323 glShaderSource(shader, 1, &shaderString, nullptr);
324 glCompileShader(shader);
325
326 GLuint program = glCreateProgram();
327
328 if (program)
329 {
330 GLint compiled;
331 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
332 glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
333 if (compiled)
334 {
335 glAttachShader(program, shader);
336 glLinkProgram(program);
337 glDetachShader(program, shader);
338 }
339 }
340
341 EXPECT_GL_NO_ERROR();
342
343 return program;
344 }
345
testTearDown()346 void testTearDown() override
347 {
348 EXTBlendFuncExtendedDrawTest::testTearDown();
349 if (mVertexProgram)
350 {
351 glDeleteProgram(mVertexProgram);
352 }
353 if (mFragProgram)
354 {
355 glDeleteProgram(mFragProgram);
356 }
357 if (mPipeline)
358 {
359 glDeleteProgramPipelines(1, &mPipeline);
360 }
361
362 ASSERT_GL_NO_ERROR();
363 }
364
365 GLuint mPipeline;
366 GLuint mVertexProgram;
367 GLuint mFragProgram;
368 };
369 } // namespace
370
371 // Test EXT_blend_func_extended related gets.
TEST_P(EXTBlendFuncExtendedTest,TestMaxDualSourceDrawBuffers)372 TEST_P(EXTBlendFuncExtendedTest, TestMaxDualSourceDrawBuffers)
373 {
374 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
375
376 GLint maxDualSourceDrawBuffers = 0;
377 glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT, &maxDualSourceDrawBuffers);
378 EXPECT_GT(maxDualSourceDrawBuffers, 0);
379
380 ASSERT_GL_NO_ERROR();
381 }
382
383 // Test a shader with EXT_blend_func_extended and gl_SecondaryFragColorEXT.
384 // Outputs to primary color buffer using primary and secondary colors.
TEST_P(EXTBlendFuncExtendedDrawTest,FragColor)385 TEST_P(EXTBlendFuncExtendedDrawTest, FragColor)
386 {
387 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
388
389 const char *kFragColorShader =
390 "#extension GL_EXT_blend_func_extended : require\n"
391 "precision mediump float;\n"
392 "uniform vec4 src0;\n"
393 "uniform vec4 src1;\n"
394 "void main() {\n"
395 " gl_FragColor = src0;\n"
396 " gl_SecondaryFragColorEXT = src1;\n"
397 "}\n";
398
399 makeProgram(essl1_shaders::vs::Simple(), kFragColorShader);
400
401 drawTest();
402 }
403
404 // Test a shader with EXT_blend_func_extended and gl_FragData.
405 // Outputs to a color buffer using primary and secondary frag data.
TEST_P(EXTBlendFuncExtendedDrawTest,FragData)406 TEST_P(EXTBlendFuncExtendedDrawTest, FragData)
407 {
408 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
409
410 // Suspected VK driver bug http://anglebug.com/5523
411 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsNVIDIA() || IsPixel2()));
412
413 // Suspected AMD VK driver bug http://anglebug.com/5537
414 ANGLE_SKIP_TEST_IF(IsVulkan() && IsWindows() && IsAMD());
415
416 const char *kFragColorShader =
417 "#extension GL_EXT_blend_func_extended : require\n"
418 "precision mediump float;\n"
419 "uniform vec4 src0;\n"
420 "uniform vec4 src1;\n"
421 "void main() {\n"
422 " gl_FragData[0] = src0;\n"
423 " gl_SecondaryFragDataEXT[0] = src1;\n"
424 "}\n";
425
426 makeProgram(essl1_shaders::vs::Simple(), kFragColorShader);
427
428 drawTest();
429 }
430
431 // Test an ESSL 3.00 shader that uses two fragment outputs with locations specified in the shader.
TEST_P(EXTBlendFuncExtendedDrawTestES3,FragmentOutputLocationsInShader)432 TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationsInShader)
433 {
434 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
435
436 const char *kFragColorShader = R"(#version 300 es
437 #extension GL_EXT_blend_func_extended : require
438 precision mediump float;
439 uniform vec4 src0;
440 uniform vec4 src1;
441 layout(location = 0, index = 1) out vec4 outSrc1;
442 layout(location = 0, index = 0) out vec4 outSrc0;
443 void main() {
444 outSrc0 = src0;
445 outSrc1 = src1;
446 })";
447
448 makeProgram(essl3_shaders::vs::Simple(), kFragColorShader);
449
450 checkOutputIndexQuery("outSrc0", 0);
451 checkOutputIndexQuery("outSrc1", 1);
452
453 drawTest();
454 }
455
456 // Test an ESSL 3.00 shader that uses two fragment outputs with locations specified through the API.
TEST_P(EXTBlendFuncExtendedDrawTestES3,FragmentOutputLocationAPI)457 TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationAPI)
458 {
459 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
460
461 constexpr char kFS[] = R"(#version 300 es
462 #extension GL_EXT_blend_func_extended : require
463 precision mediump float;
464 uniform vec4 src0;
465 uniform vec4 src1;
466 out vec4 outSrc1;
467 out vec4 outSrc0;
468 void main() {
469 outSrc0 = src0;
470 outSrc1 = src1;
471 })";
472
473 mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) {
474 glBindFragDataLocationIndexedEXT(program, 0, 0, "outSrc0");
475 glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1");
476 });
477
478 ASSERT_NE(0u, mProgram);
479
480 checkOutputIndexQuery("outSrc0", 0);
481 checkOutputIndexQuery("outSrc1", 1);
482
483 drawTest();
484 }
485
486 // Test an ESSL 3.00 shader that uses two fragment outputs, with location for one specified through
487 // the API and location for another being set automatically.
TEST_P(EXTBlendFuncExtendedDrawTestES3,FragmentOutputLocationsAPIAndAutomatic)488 TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentOutputLocationsAPIAndAutomatic)
489 {
490 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
491
492 constexpr char kFS[] = R"(#version 300 es
493 #extension GL_EXT_blend_func_extended : require
494 precision mediump float;
495 uniform vec4 src0;
496 uniform vec4 src1;
497 out vec4 outSrc1;
498 out vec4 outSrc0;
499 void main() {
500 outSrc0 = src0;
501 outSrc1 = src1;
502 })";
503
504 mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) {
505 glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1");
506 });
507
508 ASSERT_NE(0u, mProgram);
509
510 checkOutputIndexQuery("outSrc0", 0);
511 checkOutputIndexQuery("outSrc1", 1);
512
513 drawTest();
514 }
515
516 // Test an ESSL 3.00 shader that uses two array fragment outputs with locations specified through
517 // the API.
TEST_P(EXTBlendFuncExtendedDrawTestES3,FragmentArrayOutputLocationsAPI)518 TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentArrayOutputLocationsAPI)
519 {
520 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
521
522 // TODO: Investigate this mac-only failure. http://angleproject.com/1085
523 ANGLE_SKIP_TEST_IF(IsOSX());
524
525 // Suspected VK driver bug http://anglebug.com/5523
526 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsNVIDIA() || IsPixel2()));
527
528 constexpr char kFS[] = R"(#version 300 es
529 #extension GL_EXT_blend_func_extended : require
530 precision mediump float;
531 uniform vec4 src0;
532 uniform vec4 src1;
533 out vec4 outSrc1[1];
534 out vec4 outSrc0[1];
535 void main() {
536 outSrc0[0] = src0;
537 outSrc1[0] = src1;
538 })";
539
540 mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFS, [](GLuint program) {
541 // Specs aren't very clear on what kind of name should be used when binding location for
542 // array variables. We only allow names that do include the "[0]" suffix.
543 glBindFragDataLocationIndexedEXT(program, 0, 0, "outSrc0[0]");
544 glBindFragDataLocationIndexedEXT(program, 0, 1, "outSrc1[0]");
545 });
546
547 ASSERT_NE(0u, mProgram);
548
549 // The extension spec is not very clear on what name can be used for the queries for array
550 // variables. We're checking that the queries work in the same way as specified in OpenGL 4.4
551 // page 107.
552 checkOutputIndexQuery("outSrc0[0]", 0);
553 checkOutputIndexQuery("outSrc1[0]", 1);
554 checkOutputIndexQuery("outSrc0", 0);
555 checkOutputIndexQuery("outSrc1", 1);
556
557 // These queries use an out of range array index so they should return -1.
558 checkOutputIndexQuery("outSrc0[1]", -1);
559 checkOutputIndexQuery("outSrc1[1]", -1);
560
561 drawTest();
562 }
563
564 // Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest
565 // Test that tests glBindFragDataLocationEXT, glBindFragDataLocationIndexedEXT,
566 // glGetFragDataLocation, glGetFragDataIndexEXT work correctly with
567 // GLSL array output variables. The output variable can be bound by
568 // referring to the variable name with or without the first element array
569 // accessor. The getters can query location of the individual elements in
570 // the array. The test does not actually use the base test drawing,
571 // since the drivers at the time of writing do not support multiple
572 // buffers and dual source blending.
TEST_P(EXTBlendFuncExtendedDrawTestES3,ES3GettersArray)573 TEST_P(EXTBlendFuncExtendedDrawTestES3, ES3GettersArray)
574 {
575 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
576
577 // TODO(zmo): Figure out why this fails on AMD. crbug.com/585132.
578 // Also fails on the Intel Mesa driver, see
579 // https://bugs.freedesktop.org/show_bug.cgi?id=96765
580 ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD());
581 ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel());
582
583 const GLint kTestArraySize = 2;
584 const GLint kFragData0Location = 2;
585 const GLint kFragData1Location = 1;
586 const GLint kUnusedLocation = 5;
587
588 // The test binds kTestArraySize -sized array to location 1 for test purposes.
589 // The GL_MAX_DRAW_BUFFERS must be > kTestArraySize, since an
590 // array will be bound to continuous locations, starting from the first
591 // location.
592 GLint maxDrawBuffers = 0;
593 glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
594 EXPECT_LT(kTestArraySize, maxDrawBuffers);
595
596 constexpr char kFragColorShader[] = R"(#version 300 es
597 #extension GL_EXT_blend_func_extended : require
598 precision mediump float;
599 uniform vec4 src;
600 uniform vec4 src1;
601 out vec4 FragData[2];
602 void main() {
603 FragData[0] = src;
604 FragData[1] = src1;
605 })";
606
607 struct testCase
608 {
609 std::string unusedLocationName;
610 std::string fragData0LocationName;
611 std::string fragData1LocationName;
612 };
613
614 testCase testCases[4]{{"FragData[0]", "FragData", "FragData[1]"},
615 {"FragData", "FragData[0]", "FragData[1]"},
616 {"FragData[0]", "FragData", "FragData[1]"},
617 {"FragData", "FragData[0]", "FragData[1]"}};
618
619 for (const testCase &test : testCases)
620 {
621 mProgram =
622 CompileProgram(essl3_shaders::vs::Simple(), kFragColorShader, [&](GLuint program) {
623 glBindFragDataLocationEXT(program, kUnusedLocation,
624 test.unusedLocationName.c_str());
625 glBindFragDataLocationEXT(program, kFragData0Location,
626 test.fragData0LocationName.c_str());
627 glBindFragDataLocationEXT(program, kFragData1Location,
628 test.fragData1LocationName.c_str());
629 });
630
631 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
632 LinkProgram();
633 EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData"));
634 EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData"));
635 EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData[0]"));
636 EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[0]"));
637 EXPECT_EQ(kFragData1Location, glGetFragDataLocation(mProgram, "FragData[1]"));
638 EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[1]"));
639 // Index bigger than the GLSL variable array length does not find anything.
640 EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[3]"));
641 }
642 }
643
644 // Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest
TEST_P(EXTBlendFuncExtendedDrawTestES3,ESSL3BindSimpleVarAsArrayNoBind)645 TEST_P(EXTBlendFuncExtendedDrawTestES3, ESSL3BindSimpleVarAsArrayNoBind)
646 {
647 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
648
649 constexpr char kFragDataShader[] = R"(#version 300 es
650 #extension GL_EXT_blend_func_extended : require
651 precision mediump float;
652 uniform vec4 src;
653 uniform vec4 src1;
654 out vec4 FragData;
655 out vec4 SecondaryFragData;
656 void main() {
657 FragData = src;
658 SecondaryFragData = src1;
659 })";
660
661 mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFragDataShader, [](GLuint program) {
662 glBindFragDataLocationEXT(program, 0, "FragData[0]");
663 glBindFragDataLocationIndexedEXT(program, 0, 1, "SecondaryFragData[0]");
664 });
665
666 LinkProgram();
667
668 EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[0]"));
669 EXPECT_EQ(0, glGetFragDataLocation(mProgram, "FragData"));
670 EXPECT_EQ(1, glGetFragDataLocation(mProgram, "SecondaryFragData"));
671 // Did not bind index.
672 EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "SecondaryFragData"));
673
674 glBindFragDataLocationEXT(mProgram, 0, "FragData");
675 glBindFragDataLocationIndexedEXT(mProgram, 0, 1, "SecondaryFragData");
676 LinkProgram();
677 }
678
679 // Test an ESSL 3.00 program with a link-time fragment output location conflict.
TEST_P(EXTBlendFuncExtendedTestES3,FragmentOutputLocationConflict)680 TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationConflict)
681 {
682 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
683
684 constexpr char kFS[] = R"(#version 300 es
685 #extension GL_EXT_blend_func_extended : require
686 precision mediump float;
687 uniform vec4 src0;
688 uniform vec4 src1;
689 out vec4 out0;
690 out vec4 out1;
691 void main() {
692 out0 = src0;
693 out1 = src1;
694 })";
695
696 GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple());
697 GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS);
698 ASSERT_NE(0u, vs);
699 ASSERT_NE(0u, fs);
700
701 GLuint program = glCreateProgram();
702 glAttachShader(program, vs);
703 glDeleteShader(vs);
704 glAttachShader(program, fs);
705 glDeleteShader(fs);
706
707 glBindFragDataLocationIndexedEXT(program, 0, 0, "out0");
708 glBindFragDataLocationIndexedEXT(program, 0, 0, "out1");
709
710 // The program should fail to link.
711 glLinkProgram(program);
712 GLint linkStatus;
713 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
714 EXPECT_EQ(0, linkStatus);
715
716 glDeleteProgram(program);
717 }
718
719 // Test an ESSL 3.00 program with some bindings set for nonexistent variables. These should not
720 // create link-time conflicts.
TEST_P(EXTBlendFuncExtendedTestES3,FragmentOutputLocationForNonexistentOutput)721 TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationForNonexistentOutput)
722 {
723 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
724
725 constexpr char kFS[] = R"(#version 300 es
726 #extension GL_EXT_blend_func_extended : require
727 precision mediump float;
728 uniform vec4 src0;
729 out vec4 out0;
730 void main() {
731 out0 = src0;
732 })";
733
734 GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple());
735 GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS);
736 ASSERT_NE(0u, vs);
737 ASSERT_NE(0u, fs);
738
739 GLuint program = glCreateProgram();
740 glAttachShader(program, vs);
741 glDeleteShader(vs);
742 glAttachShader(program, fs);
743 glDeleteShader(fs);
744
745 glBindFragDataLocationIndexedEXT(program, 0, 0, "out0");
746 glBindFragDataLocationIndexedEXT(program, 0, 0, "out1");
747 glBindFragDataLocationIndexedEXT(program, 0, 0, "out2[0]");
748
749 // The program should link successfully - conflicting location for nonexistent variables out1 or
750 // out2 should not be an issue.
751 glLinkProgram(program);
752 GLint linkStatus;
753 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
754 EXPECT_NE(0, linkStatus);
755
756 glDeleteProgram(program);
757 }
758
759 // Test mixing shader-assigned and automatic output locations.
TEST_P(EXTBlendFuncExtendedTestES3,FragmentOutputLocationsPartiallyAutomatic)760 TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationsPartiallyAutomatic)
761 {
762 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
763
764 GLint maxDrawBuffers;
765 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
766 ANGLE_SKIP_TEST_IF(maxDrawBuffers < 4);
767
768 constexpr char kFS[] = R"(#version 300 es
769 #extension GL_EXT_blend_func_extended : require
770 precision mediump float;
771 uniform vec4 src0;
772 uniform vec4 src1;
773 uniform vec4 src2;
774 uniform vec4 src3;
775 layout(location=0) out vec4 out0;
776 layout(location=3) out vec4 out3;
777 out vec4 out12[2];
778 void main() {
779 out0 = src0;
780 out12[0] = src1;
781 out12[1] = src2;
782 out3 = src3;
783 })";
784
785 GLuint program = CompileProgram(essl3_shaders::vs::Simple(), kFS);
786 ASSERT_NE(0u, program);
787
788 GLint location = glGetFragDataLocation(program, "out0");
789 EXPECT_EQ(0, location);
790 location = glGetFragDataLocation(program, "out12");
791 EXPECT_EQ(1, location);
792 location = glGetFragDataLocation(program, "out3");
793 EXPECT_EQ(3, location);
794
795 glDeleteProgram(program);
796 }
797
798 // Test a fragment output array that doesn't fit because contiguous locations are not available.
TEST_P(EXTBlendFuncExtendedTestES3,FragmentOutputArrayDoesntFit)799 TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputArrayDoesntFit)
800 {
801 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
802
803 GLint maxDrawBuffers;
804 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
805 ANGLE_SKIP_TEST_IF(maxDrawBuffers < 4);
806
807 std::stringstream fragShader;
808 fragShader << R"(#version 300 es
809 #extension GL_EXT_blend_func_extended : require
810 precision mediump float;
811 layout(location=2) out vec4 out0;
812 out vec4 outArray[)"
813 << (maxDrawBuffers - 1) << R"(];
814 void main() {
815 out0 = vec4(1.0);
816 outArray[0] = vec4(1.0);
817 })";
818
819 GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple());
820 GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragShader.str().c_str());
821 ASSERT_NE(0u, vs);
822 ASSERT_NE(0u, fs);
823
824 GLuint program = glCreateProgram();
825 glAttachShader(program, vs);
826 glDeleteShader(vs);
827 glAttachShader(program, fs);
828 glDeleteShader(fs);
829
830 // The program should not link - there's no way to fit "outArray" into available output
831 // locations.
832 glLinkProgram(program);
833 GLint linkStatus;
834 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
835 EXPECT_EQ(0, linkStatus);
836
837 glDeleteProgram(program);
838 }
839
840 // Use a program pipeline with EXT_blend_func_extended
TEST_P(EXTBlendFuncExtendedDrawTestES31,UseProgramPipeline)841 TEST_P(EXTBlendFuncExtendedDrawTestES31, UseProgramPipeline)
842 {
843 // Only the Vulkan backend supports PPO
844 ANGLE_SKIP_TEST_IF(!IsVulkan());
845
846 // Create two separable program objects from a
847 // single source string respectively (vertSrc and fragSrc)
848 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
849
850 const char *kFragColorShader = R"(#version 300 es
851 #extension GL_EXT_blend_func_extended : require
852 precision mediump float;
853 uniform vec4 src0;
854 uniform vec4 src1;
855 layout(location = 0, index = 1) out vec4 outSrc1;
856 layout(location = 0, index = 0) out vec4 outSrc0;
857 void main() {
858 outSrc0 = src0;
859 outSrc1 = src1;
860 })";
861
862 setupProgramPipeline(essl3_shaders::vs::Simple(), kFragColorShader);
863
864 checkOutputIndexQuery("outSrc0", 0);
865 checkOutputIndexQuery("outSrc1", 1);
866
867 ASSERT_EQ(mProgram, 0u);
868 drawTest();
869
870 ASSERT_GL_NO_ERROR();
871 }
872
873 // Use program pipeline where the fragment program is changed
TEST_P(EXTBlendFuncExtendedDrawTestES31,UseTwoProgramStages)874 TEST_P(EXTBlendFuncExtendedDrawTestES31, UseTwoProgramStages)
875 {
876 // Only the Vulkan backend supports PPO
877 ANGLE_SKIP_TEST_IF(!IsVulkan());
878
879 // Create two separable program objects from a
880 // single source string respectively (vertSrc and fragSrc)
881 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
882
883 const char *kFragColorShaderFlipped = R"(#version 300 es
884 #extension GL_EXT_blend_func_extended : require
885 precision mediump float;
886 uniform vec4 src0;
887 uniform vec4 src1;
888 layout(location = 0, index = 0) out vec4 outSrc1;
889 layout(location = 0, index = 1) out vec4 outSrc0;
890 void main() {
891 outSrc0 = src0;
892 outSrc1 = src1;
893 })";
894
895 const char *kFragColorShader = R"(#version 300 es
896 #extension GL_EXT_blend_func_extended : require
897 precision mediump float;
898 uniform vec4 src0;
899 uniform vec4 src1;
900 layout(location = 0, index = 1) out vec4 outSrc1;
901 layout(location = 0, index = 0) out vec4 outSrc0;
902 void main() {
903 outSrc0 = src0;
904 outSrc1 = src1;
905 })";
906
907 setupProgramPipeline(essl3_shaders::vs::Simple(), kFragColorShaderFlipped);
908
909 // Check index values frag shader with the "flipped" index values
910 checkOutputIndexQuery("outSrc0", 1);
911 checkOutputIndexQuery("outSrc1", 0);
912
913 GLuint previousProgram = mFragProgram;
914 mFragProgram = createShaderProgram(GL_FRAGMENT_SHADER, kFragColorShader);
915 ASSERT_NE(mFragProgram, 0u);
916
917 // Change the Fragment program of the pipeline
918 glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProgram);
919 EXPECT_GL_NO_ERROR();
920
921 checkOutputIndexQuery("outSrc0", 0);
922 checkOutputIndexQuery("outSrc1", 1);
923
924 ASSERT_EQ(mProgram, 0u);
925 drawTest();
926
927 if (previousProgram)
928 {
929 glDeleteProgram(previousProgram);
930 }
931 ASSERT_GL_NO_ERROR();
932 }
933
934 ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedTest);
935
936 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EXTBlendFuncExtendedTestES3);
937 ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedTestES3);
938
939 ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedDrawTest);
940
941 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EXTBlendFuncExtendedDrawTestES3);
942 ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedDrawTestES3);
943
944 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EXTBlendFuncExtendedDrawTestES31);
945 ANGLE_INSTANTIATE_TEST_ES31(EXTBlendFuncExtendedDrawTestES31);
946