blob: d99c51d67aab72cfbcc23cdc2fb3c17d02279595 [file] [log] [blame]
Geoff Langc287ea62016-09-16 14:46:51 -04001//
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// WebGLCompatibilityTest.cpp : Tests of the GL_ANGLE_webgl_compatibility extension.
8
9#include "test_utils/ANGLETest.h"
10
11#include "test_utils/gl_raii.h"
12
Frank Henigman146e8a12017-03-02 23:22:37 -050013namespace
14{
15
16bool ConstantColorAndAlphaBlendFunctions(GLenum first, GLenum second)
17{
18 return (first == GL_CONSTANT_COLOR || first == GL_ONE_MINUS_CONSTANT_COLOR) &&
19 (second == GL_CONSTANT_ALPHA || second == GL_ONE_MINUS_CONSTANT_ALPHA);
20}
21
22void CheckBlendFunctions(GLenum src, GLenum dst)
23{
24 if (ConstantColorAndAlphaBlendFunctions(src, dst) ||
25 ConstantColorAndAlphaBlendFunctions(dst, src))
26 {
27 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
28 }
29 else
30 {
31 ASSERT_GL_NO_ERROR();
32 }
33}
34
35} // namespace
36
Geoff Langc287ea62016-09-16 14:46:51 -040037namespace angle
38{
39
40class WebGLCompatibilityTest : public ANGLETest
41{
42 protected:
43 WebGLCompatibilityTest()
44 {
45 setWindowWidth(128);
46 setWindowHeight(128);
47 setConfigRedBits(8);
48 setConfigGreenBits(8);
49 setConfigBlueBits(8);
50 setConfigAlphaBits(8);
51 setWebGLCompatibilityEnabled(true);
52 }
53
54 void SetUp() override
55 {
56 ANGLETest::SetUp();
Geoff Langc339c4e2016-11-29 10:37:36 -050057 glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>(
58 eglGetProcAddress("glRequestExtensionANGLE"));
Geoff Langc287ea62016-09-16 14:46:51 -040059 }
60
Jamie Madillcad97ee2017-02-02 18:52:44 -050061 // Called from RenderingFeedbackLoopWithDrawBuffersEXT.
62 void drawBuffersEXTFeedbackLoop(GLuint program,
63 const std::array<GLenum, 2> &drawBuffers,
64 GLenum expectedError);
65
Jamie Madill07be8bf2017-02-02 19:59:57 -050066 // Called from RenderingFeedbackLoopWithDrawBuffers.
67 void drawBuffersFeedbackLoop(GLuint program,
68 const std::array<GLenum, 2> &drawBuffers,
69 GLenum expectedError);
70
Geoff Langc339c4e2016-11-29 10:37:36 -050071 PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
Geoff Langc287ea62016-09-16 14:46:51 -040072};
73
Corentin Wallezfd456442016-12-21 17:57:00 -050074class WebGL2CompatibilityTest : public WebGLCompatibilityTest
75{
76};
77
Geoff Langc287ea62016-09-16 14:46:51 -040078// Context creation would fail if EGL_ANGLE_create_context_webgl_compatibility was not available so
79// the GL extension should always be present
80TEST_P(WebGLCompatibilityTest, ExtensionStringExposed)
81{
82 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
83}
84
85// Verify that all extension entry points are available
86TEST_P(WebGLCompatibilityTest, EntryPoints)
87{
Geoff Langc339c4e2016-11-29 10:37:36 -050088 if (extensionEnabled("GL_ANGLE_request_extension"))
Geoff Langc287ea62016-09-16 14:46:51 -040089 {
Geoff Langc339c4e2016-11-29 10:37:36 -050090 EXPECT_NE(nullptr, eglGetProcAddress("glRequestExtensionANGLE"));
Geoff Langc287ea62016-09-16 14:46:51 -040091 }
92}
93
94// WebGL 1 allows GL_DEPTH_STENCIL_ATTACHMENT as a valid binding point. Make sure it is usable,
95// even in ES2 contexts.
96TEST_P(WebGLCompatibilityTest, DepthStencilBindingPoint)
97{
98 GLRenderbuffer renderbuffer;
99 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
100 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
101
102 GLFramebuffer framebuffer;
103 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
104 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
105 renderbuffer.get());
106
107 EXPECT_GL_NO_ERROR();
108}
109
110// Test that attempting to enable an extension that doesn't exist generates GL_INVALID_OPERATION
111TEST_P(WebGLCompatibilityTest, EnableExtensionValidation)
112{
Geoff Langc339c4e2016-11-29 10:37:36 -0500113 glRequestExtensionANGLE("invalid_extension_string");
Geoff Langc287ea62016-09-16 14:46:51 -0400114 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
115}
116
117// Test enabling the GL_OES_element_index_uint extension
118TEST_P(WebGLCompatibilityTest, EnableExtensionUintIndices)
119{
120 if (getClientMajorVersion() != 2)
121 {
122 // This test only works on ES2 where uint indices are not available by default
123 return;
124 }
125
126 EXPECT_FALSE(extensionEnabled("GL_OES_element_index_uint"));
127
128 GLBuffer indexBuffer;
129 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
130
131 GLuint data[] = {0, 1, 2, 1, 3, 2};
132 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
133
134 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
135 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
136 glUseProgram(program.get());
137
138 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
139 EXPECT_GL_ERROR(GL_INVALID_ENUM);
140
Geoff Langc339c4e2016-11-29 10:37:36 -0500141 if (extensionRequestable("GL_OES_element_index_uint"))
Geoff Langc287ea62016-09-16 14:46:51 -0400142 {
Geoff Langc339c4e2016-11-29 10:37:36 -0500143 glRequestExtensionANGLE("GL_OES_element_index_uint");
Geoff Langc287ea62016-09-16 14:46:51 -0400144 EXPECT_GL_NO_ERROR();
145 EXPECT_TRUE(extensionEnabled("GL_OES_element_index_uint"));
146
147 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
148 EXPECT_GL_NO_ERROR();
149 }
150}
151
Geoff Langd7d526a2017-02-21 16:48:43 -0500152// Test enabling the GL_EXT_texture_filter_anisotropic extension
153TEST_P(WebGLCompatibilityTest, EnableExtensionTextureFilterAnisotropic)
154{
155 EXPECT_FALSE(extensionEnabled("GL_EXT_texture_filter_anisotropic"));
156
157 GLfloat maxAnisotropy = 0.0f;
158 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
159 EXPECT_GL_ERROR(GL_INVALID_ENUM);
160
161 GLTexture texture;
162 glBindTexture(GL_TEXTURE_2D, texture.get());
163 ASSERT_GL_NO_ERROR();
164
165 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
166 EXPECT_GL_ERROR(GL_INVALID_ENUM);
167
168 GLfloat currentAnisotropy = 0.0f;
169 glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &currentAnisotropy);
170 EXPECT_GL_ERROR(GL_INVALID_ENUM);
171
172 if (extensionRequestable("GL_EXT_texture_filter_anisotropic"))
173 {
174 glRequestExtensionANGLE("GL_EXT_texture_filter_anisotropic");
175 EXPECT_GL_NO_ERROR();
176 EXPECT_TRUE(extensionEnabled("GL_EXT_texture_filter_anisotropic"));
177
178 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
179 ASSERT_GL_NO_ERROR();
180 EXPECT_GE(maxAnisotropy, 2.0f);
181
182 glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &currentAnisotropy);
183 ASSERT_GL_NO_ERROR();
184 EXPECT_EQ(1.0f, currentAnisotropy);
185
186 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f);
187 ASSERT_GL_NO_ERROR();
188 }
189}
190
Bryan Bernhart87c182e2016-11-02 11:23:22 -0700191// Verify that shaders are of a compatible spec when the extension is enabled.
192TEST_P(WebGLCompatibilityTest, ExtensionCompilerSpec)
193{
194 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
195
196 // Use of reserved _webgl prefix should fail when the shader specification is for WebGL.
197 const std::string &vert =
198 "struct Foo {\n"
199 " int _webgl_bar;\n"
200 "};\n"
201 "void main()\n"
202 "{\n"
203 " Foo foo = Foo(1);\n"
204 "}";
205
206 // Default fragement shader.
207 const std::string &frag =
208 "void main()\n"
209 "{\n"
210 " gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n"
211 "}";
212
213 GLuint program = CompileProgram(vert, frag);
214 EXPECT_EQ(0u, program);
215 glDeleteProgram(program);
216}
217
Corentin Wallez327411e2016-12-09 11:09:17 -0500218// Test that client-side array buffers are forbidden in WebGL mode
219TEST_P(WebGLCompatibilityTest, ForbidsClientSideArrayBuffer)
220{
221 const std::string &vert =
222 "attribute vec3 a_pos;\n"
223 "void main()\n"
224 "{\n"
225 " gl_Position = vec4(a_pos, 1.0);\n"
226 "}\n";
227
228 const std::string &frag =
229 "precision highp float;\n"
230 "void main()\n"
231 "{\n"
232 " gl_FragColor = vec4(1.0);\n"
233 "}\n";
234
235 ANGLE_GL_PROGRAM(program, vert, frag);
236
237 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
238 ASSERT_NE(-1, posLocation);
239 glUseProgram(program.get());
240
241 const auto &vertices = GetQuadVertices();
Corentin Wallezfd456442016-12-21 17:57:00 -0500242 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 4, vertices.data());
Corentin Wallez327411e2016-12-09 11:09:17 -0500243 glEnableVertexAttribArray(posLocation);
244
245 ASSERT_GL_NO_ERROR();
246 glDrawArrays(GL_TRIANGLES, 0, 6);
247 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
248}
249
250// Test that client-side element array buffers are forbidden in WebGL mode
251TEST_P(WebGLCompatibilityTest, ForbidsClientSideElementBuffer)
252{
253 const std::string &vert =
254 "attribute vec3 a_pos;\n"
255 "void main()\n"
256 "{\n"
257 " gl_Position = vec4(a_pos, 1.0);\n"
258 "}\n";
259
260 const std::string &frag =
261 "precision highp float;\n"
262 "void main()\n"
263 "{\n"
264 " gl_FragColor = vec4(1.0);\n"
265 "}\n";
266
267 ANGLE_GL_PROGRAM(program, vert, frag);
268
269 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
270 ASSERT_NE(-1, posLocation);
271 glUseProgram(program.get());
272
273 const auto &vertices = GetQuadVertices();
274
275 GLBuffer vertexBuffer;
276 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
277 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
278 GL_STATIC_DRAW);
279
280 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
281 glEnableVertexAttribArray(posLocation);
282
Corentin Wallez327411e2016-12-09 11:09:17 -0500283 ASSERT_GL_NO_ERROR();
Corentin Wallezded1b5a2017-03-09 18:58:48 -0500284
285 // Use the pointer with value of 1 for indices instead of an actual pointer because WebGL also
286 // enforces that the top bit of indices must be 0 (i.e. offset >= 0) and would generate
287 // GL_INVALID_VALUE in that case. Using a null pointer gets caught by another check.
288 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast<const void*>(intptr_t(1)));
Corentin Wallez327411e2016-12-09 11:09:17 -0500289 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
290}
291
Corentin Wallezb1d0a2552016-12-19 16:15:54 -0500292// Tests the WebGL requirement of having the same stencil mask, writemask and ref for fron and back
293TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
294{
295 // Run the test in an FBO to make sure we have some stencil bits.
296 GLRenderbuffer renderbuffer;
297 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
298 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
299
300 GLFramebuffer framebuffer;
301 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
302 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
303 renderbuffer.get());
304
305 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
306 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
307 glUseProgram(program.get());
308 ASSERT_GL_NO_ERROR();
309
310 // Having ref and mask the same for front and back is valid.
311 glStencilMask(255);
312 glStencilFunc(GL_ALWAYS, 0, 255);
313 glDrawArrays(GL_TRIANGLES, 0, 6);
314 ASSERT_GL_NO_ERROR();
315
316 // Having a different front - back write mask generates an error.
317 glStencilMaskSeparate(GL_FRONT, 1);
318 glDrawArrays(GL_TRIANGLES, 0, 6);
319 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
320
321 // Setting both write masks separately to the same value is valid.
322 glStencilMaskSeparate(GL_BACK, 1);
323 glDrawArrays(GL_TRIANGLES, 0, 6);
324 ASSERT_GL_NO_ERROR();
325
326 // Having a different stencil front - back mask generates an error
327 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1);
328 glDrawArrays(GL_TRIANGLES, 0, 6);
329 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
330
331 // Setting both masks separately to the same value is valid.
332 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1);
333 glDrawArrays(GL_TRIANGLES, 0, 6);
334 ASSERT_GL_NO_ERROR();
335
336 // Having a different stencil front - back reference generates an error
337 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1);
338 glDrawArrays(GL_TRIANGLES, 0, 6);
339 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
340
341 // Setting both references separately to the same value is valid.
342 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1);
343 glDrawArrays(GL_TRIANGLES, 0, 6);
344 ASSERT_GL_NO_ERROR();
345
346 // Using different stencil funcs, everything being equal is valid.
347 glStencilFuncSeparate(GL_BACK, GL_NEVER, 255, 1);
348 glDrawArrays(GL_TRIANGLES, 0, 6);
349 ASSERT_GL_NO_ERROR();
350}
351
Corentin Wallez506fc9c2016-12-21 16:53:33 -0500352// Test that GL_FIXED is forbidden
353TEST_P(WebGLCompatibilityTest, ForbidsGLFixed)
354{
355 GLBuffer buffer;
356 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
357 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
358
359 glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
360 ASSERT_GL_NO_ERROR();
361
362 glVertexAttribPointer(0, 1, GL_FIXED, GL_FALSE, 0, nullptr);
363 EXPECT_GL_ERROR(GL_INVALID_ENUM);
364}
365
366// Test the WebGL limit of 255 for the attribute stride
367TEST_P(WebGLCompatibilityTest, MaxStride)
368{
369 GLBuffer buffer;
370 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
371 glBufferData(GL_ARRAY_BUFFER, 1024, nullptr, GL_STATIC_DRAW);
372
373 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 255, nullptr);
374 ASSERT_GL_NO_ERROR();
375
376 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 256, nullptr);
377 EXPECT_GL_ERROR(GL_INVALID_VALUE);
378}
379
Corentin Wallezfd456442016-12-21 17:57:00 -0500380// Test the checks for OOB reads in the vertex buffers, non-instanced version
381TEST_P(WebGLCompatibilityTest, DrawArraysBufferOutOfBoundsNonInstanced)
382{
383 const std::string &vert =
384 "attribute float a_pos;\n"
385 "void main()\n"
386 "{\n"
387 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
388 "}\n";
389
390 const std::string &frag =
391 "precision highp float;\n"
392 "void main()\n"
393 "{\n"
394 " gl_FragColor = vec4(1.0);\n"
395 "}\n";
396
397 ANGLE_GL_PROGRAM(program, vert, frag);
398
399 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
400 ASSERT_NE(-1, posLocation);
401 glUseProgram(program.get());
402
403 GLBuffer buffer;
404 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
405 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
406
407 glEnableVertexAttribArray(posLocation);
408
409 const uint8_t* zeroOffset = nullptr;
410
411 // Test touching the last element is valid.
412 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
413 glDrawArrays(GL_POINTS, 0, 4);
414 ASSERT_GL_NO_ERROR();
415
416 // Test touching the last element + 1 is invalid.
417 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
418 glDrawArrays(GL_POINTS, 0, 4);
419 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
420
421 // Test touching the last element is valid, using a stride.
422 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
423 glDrawArrays(GL_POINTS, 0, 4);
424 ASSERT_GL_NO_ERROR();
425
426 // Test touching the last element + 1 is invalid, using a stride.
427 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
428 glDrawArrays(GL_POINTS, 0, 4);
429 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
430
431 // Test any offset is valid if no vertices are drawn.
432 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
433 glDrawArrays(GL_POINTS, 0, 0);
434 ASSERT_GL_NO_ERROR();
435}
436
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500437// Test the checks for OOB reads in the index buffer
438TEST_P(WebGLCompatibilityTest, DrawElementsBufferOutOfBoundsInIndexBuffer)
Geoff Lang5f319a42017-01-09 16:49:19 -0500439{
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500440 const std::string &vert =
441 "attribute float a_pos;\n"
442 "void main()\n"
443 "{\n"
444 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
445 "}\n";
Geoff Lang5f319a42017-01-09 16:49:19 -0500446
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500447 const std::string &frag =
448 "precision highp float;\n"
449 "void main()\n"
450 "{\n"
451 " gl_FragColor = vec4(1.0);\n"
452 "}\n";
453
454 ANGLE_GL_PROGRAM(program, vert, frag);
455
456 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
457 ASSERT_NE(-1, posLocation);
458 glUseProgram(program.get());
459
460 GLBuffer vertexBuffer;
461 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
462 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
463
464 glEnableVertexAttribArray(posLocation);
465
466 const uint8_t *zeroOffset = nullptr;
467 const uint8_t zeroIndices[] = {0, 0, 0, 0, 0, 0, 0, 0};
468
469 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset);
470
471 GLBuffer indexBuffer;
472 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
473 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(zeroIndices), zeroIndices, GL_STATIC_DRAW);
Geoff Lang5f319a42017-01-09 16:49:19 -0500474 ASSERT_GL_NO_ERROR();
475
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500476 // Test touching the last index is valid
477 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 4);
478 ASSERT_GL_NO_ERROR();
Geoff Lang5f319a42017-01-09 16:49:19 -0500479
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500480 // Test touching the last + 1 element is invalid
481 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 5);
482 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
Geoff Lang5f319a42017-01-09 16:49:19 -0500483
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500484 // Test any offset if valid if count is zero
485 glDrawElements(GL_POINTS, 0, GL_UNSIGNED_BYTE, zeroOffset + 42);
486 ASSERT_GL_NO_ERROR();
Corentin Wallezfe9306a2017-02-01 17:41:05 -0500487
488 // Test touching the first index is valid
489 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 4);
490 ASSERT_GL_NO_ERROR();
491
492 // Test touching the first - 1 index is invalid
493 // The error ha been specified to be INVALID_VALUE instead of INVALID_OPERATION because it was
494 // the historic behavior of WebGL implementations
495 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset - 1);
496 EXPECT_GL_ERROR(GL_INVALID_VALUE);
Geoff Lang5f319a42017-01-09 16:49:19 -0500497}
498
Frank Henigman6137ddc2017-02-10 18:55:07 -0500499// Test depth range with 'near' more or less than 'far.'
500TEST_P(WebGLCompatibilityTest, DepthRange)
501{
502 glDepthRangef(0, 1);
503 ASSERT_GL_NO_ERROR();
504
505 glDepthRangef(.5, .5);
506 ASSERT_GL_NO_ERROR();
507
508 glDepthRangef(1, 0);
509 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
510}
511
Frank Henigman146e8a12017-03-02 23:22:37 -0500512// Test all blend function combinations.
513// In WebGL it is invalid to combine constant color with constant alpha.
514TEST_P(WebGLCompatibilityTest, BlendWithConstantColor)
515{
516 constexpr GLenum srcFunc[] = {
517 GL_ZERO,
518 GL_ONE,
519 GL_SRC_COLOR,
520 GL_ONE_MINUS_SRC_COLOR,
521 GL_DST_COLOR,
522 GL_ONE_MINUS_DST_COLOR,
523 GL_SRC_ALPHA,
524 GL_ONE_MINUS_SRC_ALPHA,
525 GL_DST_ALPHA,
526 GL_ONE_MINUS_DST_ALPHA,
527 GL_CONSTANT_COLOR,
528 GL_ONE_MINUS_CONSTANT_COLOR,
529 GL_CONSTANT_ALPHA,
530 GL_ONE_MINUS_CONSTANT_ALPHA,
531 GL_SRC_ALPHA_SATURATE,
532 };
533
534 constexpr GLenum dstFunc[] = {
535 GL_ZERO, GL_ONE,
536 GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
537 GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
538 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
539 GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
540 GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,
541 GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA,
542 };
543
544 for (GLenum src : srcFunc)
545 {
546 for (GLenum dst : dstFunc)
547 {
548 glBlendFunc(src, dst);
549 CheckBlendFunctions(src, dst);
550 glBlendFuncSeparate(src, dst, GL_ONE, GL_ONE);
551 CheckBlendFunctions(src, dst);
552 }
553 }
554}
555
Corentin Wallezfd456442016-12-21 17:57:00 -0500556// Test the checks for OOB reads in the vertex buffers, instanced version
557TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
558{
559 const std::string &vert =
560 "attribute float a_pos;\n"
561 "void main()\n"
562 "{\n"
563 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
564 "}\n";
565
566 const std::string &frag =
567 "precision highp float;\n"
568 "void main()\n"
569 "{\n"
570 " gl_FragColor = vec4(1.0);\n"
571 "}\n";
572
573 ANGLE_GL_PROGRAM(program, vert, frag);
574
575 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
576 ASSERT_NE(-1, posLocation);
577 glUseProgram(program.get());
578
579 GLBuffer buffer;
580 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
581 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
582
583 glEnableVertexAttribArray(posLocation);
584 glVertexAttribDivisor(posLocation, 1);
585
586 const uint8_t* zeroOffset = nullptr;
587
588 // Test touching the last element is valid.
589 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
590 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
591 ASSERT_GL_NO_ERROR();
592
593 // Test touching the last element + 1 is invalid.
594 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
595 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
596 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
597
598 // Test touching the last element is valid, using a stride.
599 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
600 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
601 ASSERT_GL_NO_ERROR();
602
603 // Test touching the last element + 1 is invalid, using a stride.
604 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
605 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
606 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
607
608 // Test any offset is valid if no vertices are drawn.
609 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
610 glDrawArraysInstanced(GL_POINTS, 0, 1, 0);
611 ASSERT_GL_NO_ERROR();
612}
613
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500614// Tests that NPOT is not enabled by default in WebGL 1 and that it can be enabled
615TEST_P(WebGLCompatibilityTest, NPOT)
616{
617 EXPECT_FALSE(extensionEnabled("GL_OES_texture_npot"));
618
619 // Create a texture and set an NPOT mip 0, should always be acceptable.
620 GLTexture texture;
621 glBindTexture(GL_TEXTURE_2D, texture.get());
622 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
623 ASSERT_GL_NO_ERROR();
624
625 // Try setting an NPOT mip 1 and verify the error if WebGL 1
626 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
627 if (getClientMajorVersion() < 3)
628 {
629 ASSERT_GL_ERROR(GL_INVALID_VALUE);
630 }
631 else
632 {
633 ASSERT_GL_NO_ERROR();
634 }
635
636 if (extensionRequestable("GL_OES_texture_npot"))
637 {
638 glRequestExtensionANGLE("GL_OES_texture_npot");
639 ASSERT_GL_NO_ERROR();
640
641 // Try again to set NPOT mip 1
642 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
643 ASSERT_GL_NO_ERROR();
644 }
645}
646
Jamie Madillcad97ee2017-02-02 18:52:44 -0500647template <typename T>
648void FillTexture2D(GLuint texture,
649 GLsizei width,
650 GLsizei height,
651 const T &onePixelData,
652 GLint level,
653 GLint internalFormat,
654 GLenum format,
655 GLenum type)
656{
657 std::vector<T> allPixelsData(width * height, onePixelData);
658
659 glBindTexture(GL_TEXTURE_2D, texture);
660 glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, 0, format, type,
661 allPixelsData.data());
662 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
664 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
666}
667
Frank Henigman875bbba2017-02-08 16:38:17 -0500668// Test that unset gl_Position defaults to (0,0,0,0).
669TEST_P(WebGLCompatibilityTest, DefaultPosition)
670{
671 // Draw a quad where each vertex is red if gl_Position is (0,0,0,0) before it is set,
672 // and green otherwise. The center of each quadrant will be red if and only if all
673 // four corners are red.
674 const std::string vertexShader =
675 "attribute vec3 pos;\n"
676 "varying vec4 color;\n"
677 "void main() {\n"
678 " if (gl_Position == vec4(0,0,0,0)) {\n"
679 " color = vec4(1,0,0,1);\n"
680 " } else {\n"
681 " color = vec4(0,1,0,1);\n"
682 " }\n"
683 " gl_Position = vec4(pos,1);\n"
684 "}\n";
685
686 const std::string fragmentShader =
687 "precision mediump float;\n"
688 "varying vec4 color;\n"
689 "void main() {\n"
690 " gl_FragColor = color;\n"
691 "}\n";
692
693 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
694 drawQuad(program.get(), "pos", 0.0f, 1.0f, true);
695 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 1 / 4, getWindowHeight() * 1 / 4, GLColor::red);
696 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 1 / 4, getWindowHeight() * 3 / 4, GLColor::red);
697 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() * 1 / 4, GLColor::red);
698 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() * 3 / 4, GLColor::red);
699}
700
Jamie Madilla4595b82017-01-11 17:36:34 -0500701// Tests that a rendering feedback loop triggers a GL error under WebGL.
702// Based on WebGL test conformance/renderbuffers/feedback-loop.html.
703TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoop)
704{
705 const std::string vertexShader =
706 "attribute vec4 a_position;\n"
707 "varying vec2 v_texCoord;\n"
708 "void main() {\n"
709 " gl_Position = a_position;\n"
710 " v_texCoord = (a_position.xy * 0.5) + 0.5;\n"
711 "}\n";
712
713 const std::string fragmentShader =
714 "precision mediump float;\n"
715 "varying vec2 v_texCoord;\n"
716 "uniform sampler2D u_texture;\n"
717 "void main() {\n"
718 " // Shader swizzles color channels so we can tell if the draw succeeded.\n"
719 " gl_FragColor = texture2D(u_texture, v_texCoord).gbra;\n"
720 "}\n";
721
722 GLTexture texture;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500723 FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500724
725 ASSERT_GL_NO_ERROR();
726
727 GLFramebuffer framebuffer;
728 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
729 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
730
731 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
732
733 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
734
735 GLint uniformLoc = glGetUniformLocation(program.get(), "u_texture");
736 ASSERT_NE(-1, uniformLoc);
737
738 glUseProgram(program.get());
739 glUniform1i(uniformLoc, 0);
740 glDisable(GL_BLEND);
741 glDisable(GL_DEPTH_TEST);
742 ASSERT_GL_NO_ERROR();
743
744 // Drawing with a texture that is also bound to the current framebuffer should fail
745 glBindTexture(GL_TEXTURE_2D, texture.get());
746 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
747 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
748
749 // Ensure that the texture contents did not change after the previous render
750 glBindFramebuffer(GL_FRAMEBUFFER, 0);
751 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
752 ASSERT_GL_NO_ERROR();
753 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
754
755 // Drawing when texture is bound to an inactive uniform should succeed
756 GLTexture texture2;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500757 FillTexture2D(texture2.get(), 1, 1, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500758
759 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
760 glActiveTexture(GL_TEXTURE1);
761 glBindTexture(GL_TEXTURE_2D, texture.get());
762 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
763 ASSERT_GL_NO_ERROR();
764 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
765}
766
Bryan Bernhart58806562017-01-05 13:09:31 -0800767// Test for the max draw buffers and color attachments.
768TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
769{
770 // This test only applies to ES2.
771 if (getClientMajorVersion() != 2)
772 {
773 return;
774 }
775
776 GLFramebuffer fbo[2];
777 glBindFramebuffer(GL_FRAMEBUFFER, fbo[0].get());
778
779 // Test that is valid when we bind with a single attachment point.
780 GLTexture texture;
781 glBindTexture(GL_TEXTURE_2D, texture.get());
782 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
783 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
784 ASSERT_GL_NO_ERROR();
785
786 // Test that enabling the draw buffers extension will allow us to bind with a non-zero
787 // attachment point.
788 if (extensionRequestable("GL_EXT_draw_buffers"))
789 {
790 glRequestExtensionANGLE("GL_EXT_draw_buffers");
791 EXPECT_GL_NO_ERROR();
792 EXPECT_TRUE(extensionEnabled("GL_EXT_draw_buffers"));
793
794 glBindFramebuffer(GL_FRAMEBUFFER, fbo[1].get());
795
796 GLTexture texture2;
797 glBindTexture(GL_TEXTURE_2D, texture2.get());
798 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
799 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2.get(),
800 0);
801 ASSERT_GL_NO_ERROR();
802 }
803}
804
Corentin Wallez3f6d4df2017-01-30 18:04:36 -0500805// Test that the offset in the index buffer is forced to be a multiple of the element size
806TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
807{
808 const std::string &vert =
809 "attribute vec3 a_pos;\n"
810 "void main()\n"
811 "{\n"
812 " gl_Position = vec4(a_pos, 1.0);\n"
813 "}\n";
814
815 const std::string &frag =
816 "precision highp float;\n"
817 "void main()\n"
818 "{\n"
819 " gl_FragColor = vec4(1.0);\n"
820 "}\n";
821
822 ANGLE_GL_PROGRAM(program, vert, frag);
823
824 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
825 ASSERT_NE(-1, posLocation);
826 glUseProgram(program.get());
827
828 const auto &vertices = GetQuadVertices();
829
830 GLBuffer vertexBuffer;
831 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
832 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
833 GL_STATIC_DRAW);
834
835 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
836 glEnableVertexAttribArray(posLocation);
837
838 GLBuffer indexBuffer;
839 const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0};
840 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
841 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
842
843 ASSERT_GL_NO_ERROR();
844
845 const char *zeroIndices = nullptr;
846
847 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
848 ASSERT_GL_NO_ERROR();
849
850 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
851 ASSERT_GL_NO_ERROR();
852
853 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
854 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
855}
856
857// Test that the offset and stride in the vertex buffer is forced to be a multiple of the element
858// size
859TEST_P(WebGLCompatibilityTest, VertexAttribPointerOffsetRestriction)
860{
861 const char *zeroOffset = nullptr;
862
863 // Base case, vector of two floats
864 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset);
865 ASSERT_GL_NO_ERROR();
866
867 // Test setting a non-multiple offset
868 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 1);
869 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
870 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 2);
871 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
872 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 3);
873 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
874
875 // Test setting a non-multiple stride
876 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 1, zeroOffset);
877 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
878 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, zeroOffset);
879 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
880 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3, zeroOffset);
881 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
882}
883
Jamie Madillcad97ee2017-02-02 18:52:44 -0500884void WebGLCompatibilityTest::drawBuffersEXTFeedbackLoop(GLuint program,
885 const std::array<GLenum, 2> &drawBuffers,
886 GLenum expectedError)
887{
888 glDrawBuffersEXT(2, drawBuffers.data());
889
890 // Make sure framebuffer is complete before feedback loop detection
891 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
892
893 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
894
895 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
896 // it should be NO_ERROR"
897 EXPECT_GL_ERROR(expectedError);
898}
899
900// This tests that rendering feedback loops works as expected with GL_EXT_draw_buffers.
901// Based on WebGL test conformance/extensions/webgl-draw-buffers-feedback-loop.html
902TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoopWithDrawBuffersEXT)
903{
904 const std::string vertexShader =
905 "attribute vec4 aPosition;\n"
906 "varying vec2 texCoord;\n"
907 "void main() {\n"
908 " gl_Position = aPosition;\n"
909 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
910 "}\n";
911
912 const std::string fragmentShader =
913 "#extension GL_EXT_draw_buffers : require\n"
914 "precision mediump float;\n"
915 "uniform sampler2D tex;\n"
916 "varying vec2 texCoord;\n"
917 "void main() {\n"
918 " gl_FragData[0] = texture2D(tex, texCoord);\n"
919 " gl_FragData[1] = texture2D(tex, texCoord);\n"
920 "}\n";
921
922 GLsizei width = 8;
923 GLsizei height = 8;
924
925 // This shader cannot be run in ES3, because WebGL 2 does not expose the draw buffers
926 // extension and gl_FragData semantics are changed to enforce indexing by zero always.
927 // TODO(jmadill): This extension should be disabled in WebGL 2 contexts.
928 if (/*!extensionEnabled("GL_EXT_draw_buffers")*/ getClientMajorVersion() != 2)
929 {
930 // No WEBGL_draw_buffers support -- this is legal.
931 return;
932 }
933
934 GLint maxDrawBuffers = 0;
935 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
936
937 if (maxDrawBuffers < 2)
938 {
939 std::cout << "Test skipped because MAX_DRAW_BUFFERS is too small." << std::endl;
940 return;
941 }
942
943 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
944 glUseProgram(program.get());
945 glViewport(0, 0, width, height);
946
947 GLTexture tex0;
948 GLTexture tex1;
949 GLFramebuffer fbo;
950 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
951 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
952 ASSERT_GL_NO_ERROR();
953
954 glBindTexture(GL_TEXTURE_2D, tex1.get());
955 GLint texLoc = glGetUniformLocation(program.get(), "tex");
956 ASSERT_NE(-1, texLoc);
957 glUniform1i(texLoc, 0);
958 ASSERT_GL_NO_ERROR();
959
960 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
961 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
962 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
963 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
964
965 drawBuffersEXTFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}},
966 GL_INVALID_OPERATION);
967 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
968 GL_INVALID_OPERATION);
969 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
970}
971
Jamie Madill07be8bf2017-02-02 19:59:57 -0500972// Test tests that texture copying feedback loops are properly rejected in WebGL.
973// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
974TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
975{
976 GLTexture texture;
977 glBindTexture(GL_TEXTURE_2D, texture.get());
978 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
979 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
980 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
981 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
982 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
983
984 GLTexture texture2;
985 glBindTexture(GL_TEXTURE_2D, texture2.get());
986 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
987 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
988 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
989 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
990 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
991
992 GLFramebuffer framebuffer;
993 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
994 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
995
996 // framebuffer should be FRAMEBUFFER_COMPLETE.
997 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
998 ASSERT_GL_NO_ERROR();
999
1000 // testing copyTexImage2D
1001
1002 // copyTexImage2D to same texture but different level
1003 glBindTexture(GL_TEXTURE_2D, texture.get());
1004 glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
1005 EXPECT_GL_NO_ERROR();
1006
1007 // copyTexImage2D to same texture same level, invalid feedback loop
1008 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
1009 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1010
1011 // copyTexImage2D to different texture
1012 glBindTexture(GL_TEXTURE_2D, texture2.get());
1013 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
1014 EXPECT_GL_NO_ERROR();
1015
1016 // testing copyTexSubImage2D
1017
1018 // copyTexSubImage2D to same texture but different level
1019 glBindTexture(GL_TEXTURE_2D, texture.get());
1020 glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
1021 EXPECT_GL_NO_ERROR();
1022
1023 // copyTexSubImage2D to same texture same level, invalid feedback loop
1024 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
1025 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1026
1027 // copyTexSubImage2D to different texture
1028 glBindTexture(GL_TEXTURE_2D, texture2.get());
1029 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
1030 EXPECT_GL_NO_ERROR();
1031}
1032
1033void WebGLCompatibilityTest::drawBuffersFeedbackLoop(GLuint program,
1034 const std::array<GLenum, 2> &drawBuffers,
1035 GLenum expectedError)
1036{
1037 glDrawBuffers(2, drawBuffers.data());
1038
1039 // Make sure framebuffer is complete before feedback loop detection
1040 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1041
1042 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
1043
1044 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
1045 // it should be NO_ERROR"
1046 EXPECT_GL_ERROR(expectedError);
1047}
1048
Yuly Novikov817232e2017-02-22 18:36:10 -05001049// Tests invariance matching rules between built in varyings.
1050// Based on WebGL test conformance/glsl/misc/shaders-with-invariance.html.
1051TEST_P(WebGLCompatibilityTest, BuiltInInvariant)
1052{
1053 const std::string vertexShaderVariant =
1054 "varying vec4 v_varying;\n"
1055 "void main()\n"
1056 "{\n"
1057 " gl_PointSize = 1.0;\n"
1058 " gl_Position = v_varying;\n"
1059 "}";
1060 const std::string fragmentShaderInvariantGlFragCoord =
1061 "invariant gl_FragCoord;\n"
1062 "void main()\n"
1063 "{\n"
1064 " gl_FragColor = gl_FragCoord;\n"
1065 "}";
1066 const std::string fragmentShaderInvariantGlPointCoord =
1067 "invariant gl_PointCoord;\n"
1068 "void main()\n"
1069 "{\n"
1070 " gl_FragColor = vec4(gl_PointCoord, 0.0, 0.0);\n"
1071 "}";
1072
1073 GLuint program = CompileProgram(vertexShaderVariant, fragmentShaderInvariantGlFragCoord);
1074 EXPECT_EQ(0u, program);
1075
1076 program = CompileProgram(vertexShaderVariant, fragmentShaderInvariantGlPointCoord);
1077 EXPECT_EQ(0u, program);
1078}
1079
Jamie Madill07be8bf2017-02-02 19:59:57 -05001080// This tests that rendering feedback loops works as expected with WebGL 2.
1081// Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html
1082TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers)
1083{
1084 const std::string vertexShader =
1085 "#version 300 es\n"
1086 "in vec4 aPosition;\n"
1087 "out vec2 texCoord;\n"
1088 "void main() {\n"
1089 " gl_Position = aPosition;\n"
1090 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
1091 "}\n";
1092
1093 const std::string fragmentShader =
1094 "#version 300 es\n"
1095 "precision mediump float;\n"
1096 "uniform sampler2D tex;\n"
1097 "in vec2 texCoord;\n"
1098 "out vec4 oColor;\n"
1099 "void main() {\n"
1100 " oColor = texture(tex, texCoord);\n"
1101 "}\n";
1102
1103 GLsizei width = 8;
1104 GLsizei height = 8;
1105
1106 GLint maxDrawBuffers = 0;
1107 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
1108 // ES3 requires a minimum value of 4 for MAX_DRAW_BUFFERS.
1109 ASSERT_GE(maxDrawBuffers, 2);
1110
1111 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
1112 glUseProgram(program.get());
1113 glViewport(0, 0, width, height);
1114
1115 GLTexture tex0;
1116 GLTexture tex1;
1117 GLFramebuffer fbo;
1118 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
1119 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
1120 ASSERT_GL_NO_ERROR();
1121
1122 glBindTexture(GL_TEXTURE_2D, tex1.get());
1123 GLint texLoc = glGetUniformLocation(program.get(), "tex");
1124 ASSERT_NE(-1, texLoc);
1125 glUniform1i(texLoc, 0);
1126
1127 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
1128 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
1129 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
1130 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
1131 ASSERT_GL_NO_ERROR();
1132
1133 drawBuffersFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}}, GL_INVALID_OPERATION);
1134 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
1135 GL_INVALID_OPERATION);
1136 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
1137}
1138
Jamie Madill1d37bc52017-02-02 19:59:58 -05001139// This test covers detection of rendering feedback loops between the FBO and a depth Texture.
1140// Based on WebGL test conformance2/rendering/depth-stencil-feedback-loop.html
1141TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
1142{
1143 const std::string vertexShader =
1144 "#version 300 es\n"
1145 "in vec4 aPosition;\n"
1146 "out vec2 texCoord;\n"
1147 "void main() {\n"
1148 " gl_Position = aPosition;\n"
1149 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
1150 "}\n";
1151
1152 const std::string fragmentShader =
1153 "#version 300 es\n"
1154 "precision mediump float;\n"
1155 "uniform sampler2D tex;\n"
1156 "in vec2 texCoord;\n"
1157 "out vec4 oColor;\n"
1158 "void main() {\n"
1159 " oColor = texture(tex, texCoord);\n"
1160 "}\n";
1161
1162 GLsizei width = 8;
1163 GLsizei height = 8;
1164
1165 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
1166 glUseProgram(program.get());
1167
1168 glViewport(0, 0, width, height);
1169
1170 GLint texLoc = glGetUniformLocation(program.get(), "tex");
1171 glUniform1i(texLoc, 0);
1172
1173 // Create textures and allocate storage
1174 GLTexture tex0;
1175 GLTexture tex1;
1176 GLRenderbuffer rb;
1177 FillTexture2D(tex0.get(), width, height, GLColor::black, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
1178 FillTexture2D(tex1.get(), width, height, 0x80, 0, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT,
1179 GL_UNSIGNED_INT);
1180 glBindRenderbuffer(GL_RENDERBUFFER, rb.get());
1181 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
1182 ASSERT_GL_NO_ERROR();
1183
1184 GLFramebuffer fbo;
1185 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
1186 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
1187
1188 // Test rendering and sampling feedback loop for depth buffer
1189 glBindTexture(GL_TEXTURE_2D, tex1.get());
1190 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex1.get(), 0);
1191 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1192
1193 // The same image is used as depth buffer during rendering.
1194 glEnable(GL_DEPTH_TEST);
1195 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1196 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1197
1198 // The same image is used as depth buffer. But depth mask is false.
1199 glDepthMask(GL_FALSE);
1200 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1201 EXPECT_GL_NO_ERROR();
1202
1203 // The same image is used as depth buffer. But depth test is not enabled during rendering.
1204 glDepthMask(GL_TRUE);
1205 glDisable(GL_DEPTH_TEST);
1206 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1207 EXPECT_GL_NO_ERROR();
1208
1209 // Test rendering and sampling feedback loop for stencil buffer
1210 glBindTexture(GL_RENDERBUFFER, rb.get());
1211 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
1212 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb.get());
1213 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1214 constexpr GLint stencilClearValue = 0x40;
1215 glClearBufferiv(GL_STENCIL, 0, &stencilClearValue);
1216
1217 // The same image is used as stencil buffer during rendering.
1218 glEnable(GL_STENCIL_TEST);
1219 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1220 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1221
1222 // The same image is used as stencil buffer. But stencil mask is zero.
1223 glStencilMask(0x0);
1224 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1225 EXPECT_GL_NO_ERROR();
1226
1227 // The same image is used as stencil buffer. But stencil test is not enabled during rendering.
1228 glStencilMask(0xffff);
1229 glDisable(GL_STENCIL_TEST);
1230 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1231 EXPECT_GL_NO_ERROR();
1232}
1233
Jamie Madillfd3dd432017-02-02 19:59:59 -05001234// The source and the target for CopyTexSubImage3D are the same 3D texture.
1235// But the level of the 3D texture != the level of the read attachment.
1236TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLevels)
1237{
1238 GLTexture texture;
1239 GLFramebuffer framebuffer;
1240
1241 glBindTexture(GL_TEXTURE_3D, texture.get());
1242 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1243
1244 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1245 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1246 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
1247 ASSERT_GL_NO_ERROR();
1248
1249 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1250 EXPECT_GL_NO_ERROR();
1251}
1252
1253// The source and the target for CopyTexSubImage3D are the same 3D texture.
1254// But the zoffset of the 3D texture != the layer of the read attachment.
1255TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLayers)
1256{
1257 GLTexture texture;
1258 GLFramebuffer framebuffer;
1259
1260 glBindTexture(GL_TEXTURE_3D, texture.get());
1261 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1262
1263 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1264 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 1);
1265 ASSERT_GL_NO_ERROR();
1266
1267 glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
1268 EXPECT_GL_NO_ERROR();
1269}
1270
1271// The source and the target for CopyTexSubImage3D are the same 3D texture.
1272// And the level / zoffset of the 3D texture is equal to the level / layer of the read attachment.
1273TEST_P(WebGL2CompatibilityTest, TextureCopyingFeedbackLoop3D)
1274{
1275 GLTexture texture;
1276 GLFramebuffer framebuffer;
1277
1278 glBindTexture(GL_TEXTURE_3D, texture.get());
1279 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1280
1281 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1282 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1283 glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1284 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 1, 0);
1285 ASSERT_GL_NO_ERROR();
1286
1287 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1288 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1289}
1290
Geoff Langc287ea62016-09-16 14:46:51 -04001291// Use this to select which configurations (e.g. which renderer, which GLES major version) these
1292// tests should be run against.
1293ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
1294 ES2_D3D9(),
1295 ES2_D3D11(),
1296 ES3_D3D11(),
1297 ES2_D3D11_FL9_3(),
1298 ES2_OPENGL(),
1299 ES3_OPENGL(),
1300 ES2_OPENGLES(),
1301 ES3_OPENGLES());
1302
Jamie Madill07be8bf2017-02-02 19:59:57 -05001303ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
Geoff Langc287ea62016-09-16 14:46:51 -04001304} // namespace