blob: 87293cfb52285c1bb367428e5e325fb37d45f7bb [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
13namespace angle
14{
15
16class WebGLCompatibilityTest : public ANGLETest
17{
18 protected:
19 WebGLCompatibilityTest()
20 {
21 setWindowWidth(128);
22 setWindowHeight(128);
23 setConfigRedBits(8);
24 setConfigGreenBits(8);
25 setConfigBlueBits(8);
26 setConfigAlphaBits(8);
27 setWebGLCompatibilityEnabled(true);
28 }
29
30 void SetUp() override
31 {
32 ANGLETest::SetUp();
Geoff Langc339c4e2016-11-29 10:37:36 -050033 glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>(
34 eglGetProcAddress("glRequestExtensionANGLE"));
Geoff Langc287ea62016-09-16 14:46:51 -040035 }
36
37 void TearDown() override { ANGLETest::TearDown(); }
38
Jamie Madillcad97ee2017-02-02 18:52:44 -050039 // Called from RenderingFeedbackLoopWithDrawBuffersEXT.
40 void drawBuffersEXTFeedbackLoop(GLuint program,
41 const std::array<GLenum, 2> &drawBuffers,
42 GLenum expectedError);
43
Jamie Madill07be8bf2017-02-02 19:59:57 -050044 // Called from RenderingFeedbackLoopWithDrawBuffers.
45 void drawBuffersFeedbackLoop(GLuint program,
46 const std::array<GLenum, 2> &drawBuffers,
47 GLenum expectedError);
48
Geoff Langc339c4e2016-11-29 10:37:36 -050049 PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
Geoff Langc287ea62016-09-16 14:46:51 -040050};
51
Corentin Wallezfd456442016-12-21 17:57:00 -050052class WebGL2CompatibilityTest : public WebGLCompatibilityTest
53{
54};
55
Geoff Langc287ea62016-09-16 14:46:51 -040056// Context creation would fail if EGL_ANGLE_create_context_webgl_compatibility was not available so
57// the GL extension should always be present
58TEST_P(WebGLCompatibilityTest, ExtensionStringExposed)
59{
60 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
61}
62
63// Verify that all extension entry points are available
64TEST_P(WebGLCompatibilityTest, EntryPoints)
65{
Geoff Langc339c4e2016-11-29 10:37:36 -050066 if (extensionEnabled("GL_ANGLE_request_extension"))
Geoff Langc287ea62016-09-16 14:46:51 -040067 {
Geoff Langc339c4e2016-11-29 10:37:36 -050068 EXPECT_NE(nullptr, eglGetProcAddress("glRequestExtensionANGLE"));
Geoff Langc287ea62016-09-16 14:46:51 -040069 }
70}
71
72// WebGL 1 allows GL_DEPTH_STENCIL_ATTACHMENT as a valid binding point. Make sure it is usable,
73// even in ES2 contexts.
74TEST_P(WebGLCompatibilityTest, DepthStencilBindingPoint)
75{
76 GLRenderbuffer renderbuffer;
77 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
78 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
79
80 GLFramebuffer framebuffer;
81 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
82 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
83 renderbuffer.get());
84
85 EXPECT_GL_NO_ERROR();
86}
87
88// Test that attempting to enable an extension that doesn't exist generates GL_INVALID_OPERATION
89TEST_P(WebGLCompatibilityTest, EnableExtensionValidation)
90{
Geoff Langc339c4e2016-11-29 10:37:36 -050091 glRequestExtensionANGLE("invalid_extension_string");
Geoff Langc287ea62016-09-16 14:46:51 -040092 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
93}
94
95// Test enabling the GL_OES_element_index_uint extension
96TEST_P(WebGLCompatibilityTest, EnableExtensionUintIndices)
97{
98 if (getClientMajorVersion() != 2)
99 {
100 // This test only works on ES2 where uint indices are not available by default
101 return;
102 }
103
104 EXPECT_FALSE(extensionEnabled("GL_OES_element_index_uint"));
105
106 GLBuffer indexBuffer;
107 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
108
109 GLuint data[] = {0, 1, 2, 1, 3, 2};
110 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
111
112 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
113 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
114 glUseProgram(program.get());
115
116 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
117 EXPECT_GL_ERROR(GL_INVALID_ENUM);
118
Geoff Langc339c4e2016-11-29 10:37:36 -0500119 if (extensionRequestable("GL_OES_element_index_uint"))
Geoff Langc287ea62016-09-16 14:46:51 -0400120 {
Geoff Langc339c4e2016-11-29 10:37:36 -0500121 glRequestExtensionANGLE("GL_OES_element_index_uint");
Geoff Langc287ea62016-09-16 14:46:51 -0400122 EXPECT_GL_NO_ERROR();
123 EXPECT_TRUE(extensionEnabled("GL_OES_element_index_uint"));
124
125 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
126 EXPECT_GL_NO_ERROR();
127 }
128}
129
Bryan Bernhart87c182e2016-11-02 11:23:22 -0700130// Verify that shaders are of a compatible spec when the extension is enabled.
131TEST_P(WebGLCompatibilityTest, ExtensionCompilerSpec)
132{
133 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
134
135 // Use of reserved _webgl prefix should fail when the shader specification is for WebGL.
136 const std::string &vert =
137 "struct Foo {\n"
138 " int _webgl_bar;\n"
139 "};\n"
140 "void main()\n"
141 "{\n"
142 " Foo foo = Foo(1);\n"
143 "}";
144
145 // Default fragement shader.
146 const std::string &frag =
147 "void main()\n"
148 "{\n"
149 " gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n"
150 "}";
151
152 GLuint program = CompileProgram(vert, frag);
153 EXPECT_EQ(0u, program);
154 glDeleteProgram(program);
155}
156
Corentin Wallez327411e2016-12-09 11:09:17 -0500157// Test that client-side array buffers are forbidden in WebGL mode
158TEST_P(WebGLCompatibilityTest, ForbidsClientSideArrayBuffer)
159{
160 const std::string &vert =
161 "attribute vec3 a_pos;\n"
162 "void main()\n"
163 "{\n"
164 " gl_Position = vec4(a_pos, 1.0);\n"
165 "}\n";
166
167 const std::string &frag =
168 "precision highp float;\n"
169 "void main()\n"
170 "{\n"
171 " gl_FragColor = vec4(1.0);\n"
172 "}\n";
173
174 ANGLE_GL_PROGRAM(program, vert, frag);
175
176 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
177 ASSERT_NE(-1, posLocation);
178 glUseProgram(program.get());
179
180 const auto &vertices = GetQuadVertices();
Corentin Wallezfd456442016-12-21 17:57:00 -0500181 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 4, vertices.data());
Corentin Wallez327411e2016-12-09 11:09:17 -0500182 glEnableVertexAttribArray(posLocation);
183
184 ASSERT_GL_NO_ERROR();
185 glDrawArrays(GL_TRIANGLES, 0, 6);
186 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
187}
188
189// Test that client-side element array buffers are forbidden in WebGL mode
190TEST_P(WebGLCompatibilityTest, ForbidsClientSideElementBuffer)
191{
192 const std::string &vert =
193 "attribute vec3 a_pos;\n"
194 "void main()\n"
195 "{\n"
196 " gl_Position = vec4(a_pos, 1.0);\n"
197 "}\n";
198
199 const std::string &frag =
200 "precision highp float;\n"
201 "void main()\n"
202 "{\n"
203 " gl_FragColor = vec4(1.0);\n"
204 "}\n";
205
206 ANGLE_GL_PROGRAM(program, vert, frag);
207
208 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
209 ASSERT_NE(-1, posLocation);
210 glUseProgram(program.get());
211
212 const auto &vertices = GetQuadVertices();
213
214 GLBuffer vertexBuffer;
215 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
216 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
217 GL_STATIC_DRAW);
218
219 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
220 glEnableVertexAttribArray(posLocation);
221
222 const GLubyte indices[] = {0, 1, 2, 3, 4, 5};
223
224 ASSERT_GL_NO_ERROR();
225 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
226 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
227}
228
Corentin Wallezb1d0a2552016-12-19 16:15:54 -0500229// Tests the WebGL requirement of having the same stencil mask, writemask and ref for fron and back
230TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
231{
232 // Run the test in an FBO to make sure we have some stencil bits.
233 GLRenderbuffer renderbuffer;
234 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
235 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
236
237 GLFramebuffer framebuffer;
238 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
239 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
240 renderbuffer.get());
241
242 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
243 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
244 glUseProgram(program.get());
245 ASSERT_GL_NO_ERROR();
246
247 // Having ref and mask the same for front and back is valid.
248 glStencilMask(255);
249 glStencilFunc(GL_ALWAYS, 0, 255);
250 glDrawArrays(GL_TRIANGLES, 0, 6);
251 ASSERT_GL_NO_ERROR();
252
253 // Having a different front - back write mask generates an error.
254 glStencilMaskSeparate(GL_FRONT, 1);
255 glDrawArrays(GL_TRIANGLES, 0, 6);
256 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
257
258 // Setting both write masks separately to the same value is valid.
259 glStencilMaskSeparate(GL_BACK, 1);
260 glDrawArrays(GL_TRIANGLES, 0, 6);
261 ASSERT_GL_NO_ERROR();
262
263 // Having a different stencil front - back mask generates an error
264 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1);
265 glDrawArrays(GL_TRIANGLES, 0, 6);
266 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
267
268 // Setting both masks separately to the same value is valid.
269 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1);
270 glDrawArrays(GL_TRIANGLES, 0, 6);
271 ASSERT_GL_NO_ERROR();
272
273 // Having a different stencil front - back reference generates an error
274 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1);
275 glDrawArrays(GL_TRIANGLES, 0, 6);
276 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
277
278 // Setting both references separately to the same value is valid.
279 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1);
280 glDrawArrays(GL_TRIANGLES, 0, 6);
281 ASSERT_GL_NO_ERROR();
282
283 // Using different stencil funcs, everything being equal is valid.
284 glStencilFuncSeparate(GL_BACK, GL_NEVER, 255, 1);
285 glDrawArrays(GL_TRIANGLES, 0, 6);
286 ASSERT_GL_NO_ERROR();
287}
288
Corentin Wallez506fc9c2016-12-21 16:53:33 -0500289// Test that GL_FIXED is forbidden
290TEST_P(WebGLCompatibilityTest, ForbidsGLFixed)
291{
292 GLBuffer buffer;
293 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
294 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
295
296 glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
297 ASSERT_GL_NO_ERROR();
298
299 glVertexAttribPointer(0, 1, GL_FIXED, GL_FALSE, 0, nullptr);
300 EXPECT_GL_ERROR(GL_INVALID_ENUM);
301}
302
303// Test the WebGL limit of 255 for the attribute stride
304TEST_P(WebGLCompatibilityTest, MaxStride)
305{
306 GLBuffer buffer;
307 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
308 glBufferData(GL_ARRAY_BUFFER, 1024, nullptr, GL_STATIC_DRAW);
309
310 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 255, nullptr);
311 ASSERT_GL_NO_ERROR();
312
313 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 256, nullptr);
314 EXPECT_GL_ERROR(GL_INVALID_VALUE);
315}
316
Corentin Wallezfd456442016-12-21 17:57:00 -0500317// Test the checks for OOB reads in the vertex buffers, non-instanced version
318TEST_P(WebGLCompatibilityTest, DrawArraysBufferOutOfBoundsNonInstanced)
319{
320 const std::string &vert =
321 "attribute float a_pos;\n"
322 "void main()\n"
323 "{\n"
324 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
325 "}\n";
326
327 const std::string &frag =
328 "precision highp float;\n"
329 "void main()\n"
330 "{\n"
331 " gl_FragColor = vec4(1.0);\n"
332 "}\n";
333
334 ANGLE_GL_PROGRAM(program, vert, frag);
335
336 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
337 ASSERT_NE(-1, posLocation);
338 glUseProgram(program.get());
339
340 GLBuffer buffer;
341 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
342 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
343
344 glEnableVertexAttribArray(posLocation);
345
346 const uint8_t* zeroOffset = nullptr;
347
348 // Test touching the last element is valid.
349 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
350 glDrawArrays(GL_POINTS, 0, 4);
351 ASSERT_GL_NO_ERROR();
352
353 // Test touching the last element + 1 is invalid.
354 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
355 glDrawArrays(GL_POINTS, 0, 4);
356 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
357
358 // Test touching the last element is valid, using a stride.
359 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
360 glDrawArrays(GL_POINTS, 0, 4);
361 ASSERT_GL_NO_ERROR();
362
363 // Test touching the last element + 1 is invalid, using a stride.
364 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
365 glDrawArrays(GL_POINTS, 0, 4);
366 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
367
368 // Test any offset is valid if no vertices are drawn.
369 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
370 glDrawArrays(GL_POINTS, 0, 0);
371 ASSERT_GL_NO_ERROR();
372}
373
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500374// Test the checks for OOB reads in the index buffer
375TEST_P(WebGLCompatibilityTest, DrawElementsBufferOutOfBoundsInIndexBuffer)
Geoff Lang5f319a42017-01-09 16:49:19 -0500376{
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500377 const std::string &vert =
378 "attribute float a_pos;\n"
379 "void main()\n"
380 "{\n"
381 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
382 "}\n";
Geoff Lang5f319a42017-01-09 16:49:19 -0500383
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500384 const std::string &frag =
385 "precision highp float;\n"
386 "void main()\n"
387 "{\n"
388 " gl_FragColor = vec4(1.0);\n"
389 "}\n";
390
391 ANGLE_GL_PROGRAM(program, vert, frag);
392
393 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
394 ASSERT_NE(-1, posLocation);
395 glUseProgram(program.get());
396
397 GLBuffer vertexBuffer;
398 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
399 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
400
401 glEnableVertexAttribArray(posLocation);
402
403 const uint8_t *zeroOffset = nullptr;
404 const uint8_t zeroIndices[] = {0, 0, 0, 0, 0, 0, 0, 0};
405
406 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset);
407
408 GLBuffer indexBuffer;
409 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
410 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(zeroIndices), zeroIndices, GL_STATIC_DRAW);
Geoff Lang5f319a42017-01-09 16:49:19 -0500411 ASSERT_GL_NO_ERROR();
412
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500413 // Test touching the last index is valid
414 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 4);
415 ASSERT_GL_NO_ERROR();
Geoff Lang5f319a42017-01-09 16:49:19 -0500416
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500417 // Test touching the last + 1 element is invalid
418 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 5);
419 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
Geoff Lang5f319a42017-01-09 16:49:19 -0500420
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500421 // Test any offset if valid if count is zero
422 glDrawElements(GL_POINTS, 0, GL_UNSIGNED_BYTE, zeroOffset + 42);
423 ASSERT_GL_NO_ERROR();
Geoff Lang5f319a42017-01-09 16:49:19 -0500424}
425
Frank Henigman6137ddc2017-02-10 18:55:07 -0500426// Test depth range with 'near' more or less than 'far.'
427TEST_P(WebGLCompatibilityTest, DepthRange)
428{
429 glDepthRangef(0, 1);
430 ASSERT_GL_NO_ERROR();
431
432 glDepthRangef(.5, .5);
433 ASSERT_GL_NO_ERROR();
434
435 glDepthRangef(1, 0);
436 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
437}
438
Corentin Wallezfd456442016-12-21 17:57:00 -0500439// Test the checks for OOB reads in the vertex buffers, instanced version
440TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
441{
442 const std::string &vert =
443 "attribute float a_pos;\n"
444 "void main()\n"
445 "{\n"
446 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
447 "}\n";
448
449 const std::string &frag =
450 "precision highp float;\n"
451 "void main()\n"
452 "{\n"
453 " gl_FragColor = vec4(1.0);\n"
454 "}\n";
455
456 ANGLE_GL_PROGRAM(program, vert, frag);
457
458 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
459 ASSERT_NE(-1, posLocation);
460 glUseProgram(program.get());
461
462 GLBuffer buffer;
463 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
464 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
465
466 glEnableVertexAttribArray(posLocation);
467 glVertexAttribDivisor(posLocation, 1);
468
469 const uint8_t* zeroOffset = nullptr;
470
471 // Test touching the last element is valid.
472 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
473 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
474 ASSERT_GL_NO_ERROR();
475
476 // Test touching the last element + 1 is invalid.
477 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
478 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
479 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
480
481 // Test touching the last element is valid, using a stride.
482 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
483 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
484 ASSERT_GL_NO_ERROR();
485
486 // Test touching the last element + 1 is invalid, using a stride.
487 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
488 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
489 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
490
491 // Test any offset is valid if no vertices are drawn.
492 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
493 glDrawArraysInstanced(GL_POINTS, 0, 1, 0);
494 ASSERT_GL_NO_ERROR();
495}
496
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500497// Tests that NPOT is not enabled by default in WebGL 1 and that it can be enabled
498TEST_P(WebGLCompatibilityTest, NPOT)
499{
500 EXPECT_FALSE(extensionEnabled("GL_OES_texture_npot"));
501
502 // Create a texture and set an NPOT mip 0, should always be acceptable.
503 GLTexture texture;
504 glBindTexture(GL_TEXTURE_2D, texture.get());
505 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
506 ASSERT_GL_NO_ERROR();
507
508 // Try setting an NPOT mip 1 and verify the error if WebGL 1
509 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
510 if (getClientMajorVersion() < 3)
511 {
512 ASSERT_GL_ERROR(GL_INVALID_VALUE);
513 }
514 else
515 {
516 ASSERT_GL_NO_ERROR();
517 }
518
519 if (extensionRequestable("GL_OES_texture_npot"))
520 {
521 glRequestExtensionANGLE("GL_OES_texture_npot");
522 ASSERT_GL_NO_ERROR();
523
524 // Try again to set NPOT mip 1
525 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
526 ASSERT_GL_NO_ERROR();
527 }
528}
529
Jamie Madillcad97ee2017-02-02 18:52:44 -0500530template <typename T>
531void FillTexture2D(GLuint texture,
532 GLsizei width,
533 GLsizei height,
534 const T &onePixelData,
535 GLint level,
536 GLint internalFormat,
537 GLenum format,
538 GLenum type)
539{
540 std::vector<T> allPixelsData(width * height, onePixelData);
541
542 glBindTexture(GL_TEXTURE_2D, texture);
543 glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, 0, format, type,
544 allPixelsData.data());
545 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
546 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
547 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
548 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
549}
550
Frank Henigman875bbba2017-02-08 16:38:17 -0500551// Test that unset gl_Position defaults to (0,0,0,0).
552TEST_P(WebGLCompatibilityTest, DefaultPosition)
553{
554 // Draw a quad where each vertex is red if gl_Position is (0,0,0,0) before it is set,
555 // and green otherwise. The center of each quadrant will be red if and only if all
556 // four corners are red.
557 const std::string vertexShader =
558 "attribute vec3 pos;\n"
559 "varying vec4 color;\n"
560 "void main() {\n"
561 " if (gl_Position == vec4(0,0,0,0)) {\n"
562 " color = vec4(1,0,0,1);\n"
563 " } else {\n"
564 " color = vec4(0,1,0,1);\n"
565 " }\n"
566 " gl_Position = vec4(pos,1);\n"
567 "}\n";
568
569 const std::string fragmentShader =
570 "precision mediump float;\n"
571 "varying vec4 color;\n"
572 "void main() {\n"
573 " gl_FragColor = color;\n"
574 "}\n";
575
576 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
577 drawQuad(program.get(), "pos", 0.0f, 1.0f, true);
578 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 1 / 4, getWindowHeight() * 1 / 4, GLColor::red);
579 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 1 / 4, getWindowHeight() * 3 / 4, GLColor::red);
580 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() * 1 / 4, GLColor::red);
581 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() * 3 / 4, getWindowHeight() * 3 / 4, GLColor::red);
582}
583
Jamie Madilla4595b82017-01-11 17:36:34 -0500584// Tests that a rendering feedback loop triggers a GL error under WebGL.
585// Based on WebGL test conformance/renderbuffers/feedback-loop.html.
586TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoop)
587{
588 const std::string vertexShader =
589 "attribute vec4 a_position;\n"
590 "varying vec2 v_texCoord;\n"
591 "void main() {\n"
592 " gl_Position = a_position;\n"
593 " v_texCoord = (a_position.xy * 0.5) + 0.5;\n"
594 "}\n";
595
596 const std::string fragmentShader =
597 "precision mediump float;\n"
598 "varying vec2 v_texCoord;\n"
599 "uniform sampler2D u_texture;\n"
600 "void main() {\n"
601 " // Shader swizzles color channels so we can tell if the draw succeeded.\n"
602 " gl_FragColor = texture2D(u_texture, v_texCoord).gbra;\n"
603 "}\n";
604
605 GLTexture texture;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500606 FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500607
608 ASSERT_GL_NO_ERROR();
609
610 GLFramebuffer framebuffer;
611 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
612 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
613
614 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
615
616 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
617
618 GLint uniformLoc = glGetUniformLocation(program.get(), "u_texture");
619 ASSERT_NE(-1, uniformLoc);
620
621 glUseProgram(program.get());
622 glUniform1i(uniformLoc, 0);
623 glDisable(GL_BLEND);
624 glDisable(GL_DEPTH_TEST);
625 ASSERT_GL_NO_ERROR();
626
627 // Drawing with a texture that is also bound to the current framebuffer should fail
628 glBindTexture(GL_TEXTURE_2D, texture.get());
629 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
630 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
631
632 // Ensure that the texture contents did not change after the previous render
633 glBindFramebuffer(GL_FRAMEBUFFER, 0);
634 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
635 ASSERT_GL_NO_ERROR();
636 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
637
638 // Drawing when texture is bound to an inactive uniform should succeed
639 GLTexture texture2;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500640 FillTexture2D(texture2.get(), 1, 1, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500641
642 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
643 glActiveTexture(GL_TEXTURE1);
644 glBindTexture(GL_TEXTURE_2D, texture.get());
645 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
646 ASSERT_GL_NO_ERROR();
647 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
648}
649
Bryan Bernhart58806562017-01-05 13:09:31 -0800650// Test for the max draw buffers and color attachments.
651TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
652{
653 // This test only applies to ES2.
654 if (getClientMajorVersion() != 2)
655 {
656 return;
657 }
658
659 GLFramebuffer fbo[2];
660 glBindFramebuffer(GL_FRAMEBUFFER, fbo[0].get());
661
662 // Test that is valid when we bind with a single attachment point.
663 GLTexture texture;
664 glBindTexture(GL_TEXTURE_2D, texture.get());
665 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
666 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
667 ASSERT_GL_NO_ERROR();
668
669 // Test that enabling the draw buffers extension will allow us to bind with a non-zero
670 // attachment point.
671 if (extensionRequestable("GL_EXT_draw_buffers"))
672 {
673 glRequestExtensionANGLE("GL_EXT_draw_buffers");
674 EXPECT_GL_NO_ERROR();
675 EXPECT_TRUE(extensionEnabled("GL_EXT_draw_buffers"));
676
677 glBindFramebuffer(GL_FRAMEBUFFER, fbo[1].get());
678
679 GLTexture texture2;
680 glBindTexture(GL_TEXTURE_2D, texture2.get());
681 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
682 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2.get(),
683 0);
684 ASSERT_GL_NO_ERROR();
685 }
686}
687
Corentin Wallez3f6d4df2017-01-30 18:04:36 -0500688// Test that the offset in the index buffer is forced to be a multiple of the element size
689TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
690{
691 const std::string &vert =
692 "attribute vec3 a_pos;\n"
693 "void main()\n"
694 "{\n"
695 " gl_Position = vec4(a_pos, 1.0);\n"
696 "}\n";
697
698 const std::string &frag =
699 "precision highp float;\n"
700 "void main()\n"
701 "{\n"
702 " gl_FragColor = vec4(1.0);\n"
703 "}\n";
704
705 ANGLE_GL_PROGRAM(program, vert, frag);
706
707 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
708 ASSERT_NE(-1, posLocation);
709 glUseProgram(program.get());
710
711 const auto &vertices = GetQuadVertices();
712
713 GLBuffer vertexBuffer;
714 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
715 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
716 GL_STATIC_DRAW);
717
718 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
719 glEnableVertexAttribArray(posLocation);
720
721 GLBuffer indexBuffer;
722 const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0};
723 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
724 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
725
726 ASSERT_GL_NO_ERROR();
727
728 const char *zeroIndices = nullptr;
729
730 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
731 ASSERT_GL_NO_ERROR();
732
733 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
734 ASSERT_GL_NO_ERROR();
735
736 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
737 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
738}
739
740// Test that the offset and stride in the vertex buffer is forced to be a multiple of the element
741// size
742TEST_P(WebGLCompatibilityTest, VertexAttribPointerOffsetRestriction)
743{
744 const char *zeroOffset = nullptr;
745
746 // Base case, vector of two floats
747 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset);
748 ASSERT_GL_NO_ERROR();
749
750 // Test setting a non-multiple offset
751 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 1);
752 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
753 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 2);
754 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
755 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 3);
756 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
757
758 // Test setting a non-multiple stride
759 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 1, zeroOffset);
760 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
761 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, zeroOffset);
762 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
763 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3, zeroOffset);
764 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
765}
766
Jamie Madillcad97ee2017-02-02 18:52:44 -0500767void WebGLCompatibilityTest::drawBuffersEXTFeedbackLoop(GLuint program,
768 const std::array<GLenum, 2> &drawBuffers,
769 GLenum expectedError)
770{
771 glDrawBuffersEXT(2, drawBuffers.data());
772
773 // Make sure framebuffer is complete before feedback loop detection
774 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
775
776 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
777
778 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
779 // it should be NO_ERROR"
780 EXPECT_GL_ERROR(expectedError);
781}
782
783// This tests that rendering feedback loops works as expected with GL_EXT_draw_buffers.
784// Based on WebGL test conformance/extensions/webgl-draw-buffers-feedback-loop.html
785TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoopWithDrawBuffersEXT)
786{
787 const std::string vertexShader =
788 "attribute vec4 aPosition;\n"
789 "varying vec2 texCoord;\n"
790 "void main() {\n"
791 " gl_Position = aPosition;\n"
792 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
793 "}\n";
794
795 const std::string fragmentShader =
796 "#extension GL_EXT_draw_buffers : require\n"
797 "precision mediump float;\n"
798 "uniform sampler2D tex;\n"
799 "varying vec2 texCoord;\n"
800 "void main() {\n"
801 " gl_FragData[0] = texture2D(tex, texCoord);\n"
802 " gl_FragData[1] = texture2D(tex, texCoord);\n"
803 "}\n";
804
805 GLsizei width = 8;
806 GLsizei height = 8;
807
808 // This shader cannot be run in ES3, because WebGL 2 does not expose the draw buffers
809 // extension and gl_FragData semantics are changed to enforce indexing by zero always.
810 // TODO(jmadill): This extension should be disabled in WebGL 2 contexts.
811 if (/*!extensionEnabled("GL_EXT_draw_buffers")*/ getClientMajorVersion() != 2)
812 {
813 // No WEBGL_draw_buffers support -- this is legal.
814 return;
815 }
816
817 GLint maxDrawBuffers = 0;
818 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
819
820 if (maxDrawBuffers < 2)
821 {
822 std::cout << "Test skipped because MAX_DRAW_BUFFERS is too small." << std::endl;
823 return;
824 }
825
826 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
827 glUseProgram(program.get());
828 glViewport(0, 0, width, height);
829
830 GLTexture tex0;
831 GLTexture tex1;
832 GLFramebuffer fbo;
833 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
834 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
835 ASSERT_GL_NO_ERROR();
836
837 glBindTexture(GL_TEXTURE_2D, tex1.get());
838 GLint texLoc = glGetUniformLocation(program.get(), "tex");
839 ASSERT_NE(-1, texLoc);
840 glUniform1i(texLoc, 0);
841 ASSERT_GL_NO_ERROR();
842
843 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
844 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
845 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
846 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
847
848 drawBuffersEXTFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}},
849 GL_INVALID_OPERATION);
850 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
851 GL_INVALID_OPERATION);
852 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
853}
854
Jamie Madill07be8bf2017-02-02 19:59:57 -0500855// Test tests that texture copying feedback loops are properly rejected in WebGL.
856// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
857TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
858{
859 GLTexture texture;
860 glBindTexture(GL_TEXTURE_2D, texture.get());
861 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
862 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
863 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
864 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
865 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
866
867 GLTexture texture2;
868 glBindTexture(GL_TEXTURE_2D, texture2.get());
869 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
870 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
871 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
872 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
873 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
874
875 GLFramebuffer framebuffer;
876 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
877 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
878
879 // framebuffer should be FRAMEBUFFER_COMPLETE.
880 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
881 ASSERT_GL_NO_ERROR();
882
883 // testing copyTexImage2D
884
885 // copyTexImage2D to same texture but different level
886 glBindTexture(GL_TEXTURE_2D, texture.get());
887 glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
888 EXPECT_GL_NO_ERROR();
889
890 // copyTexImage2D to same texture same level, invalid feedback loop
891 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
892 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
893
894 // copyTexImage2D to different texture
895 glBindTexture(GL_TEXTURE_2D, texture2.get());
896 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
897 EXPECT_GL_NO_ERROR();
898
899 // testing copyTexSubImage2D
900
901 // copyTexSubImage2D to same texture but different level
902 glBindTexture(GL_TEXTURE_2D, texture.get());
903 glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
904 EXPECT_GL_NO_ERROR();
905
906 // copyTexSubImage2D to same texture same level, invalid feedback loop
907 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
908 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
909
910 // copyTexSubImage2D to different texture
911 glBindTexture(GL_TEXTURE_2D, texture2.get());
912 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
913 EXPECT_GL_NO_ERROR();
914}
915
916void WebGLCompatibilityTest::drawBuffersFeedbackLoop(GLuint program,
917 const std::array<GLenum, 2> &drawBuffers,
918 GLenum expectedError)
919{
920 glDrawBuffers(2, drawBuffers.data());
921
922 // Make sure framebuffer is complete before feedback loop detection
923 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
924
925 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
926
927 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
928 // it should be NO_ERROR"
929 EXPECT_GL_ERROR(expectedError);
930}
931
932// This tests that rendering feedback loops works as expected with WebGL 2.
933// Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html
934TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers)
935{
936 const std::string vertexShader =
937 "#version 300 es\n"
938 "in vec4 aPosition;\n"
939 "out vec2 texCoord;\n"
940 "void main() {\n"
941 " gl_Position = aPosition;\n"
942 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
943 "}\n";
944
945 const std::string fragmentShader =
946 "#version 300 es\n"
947 "precision mediump float;\n"
948 "uniform sampler2D tex;\n"
949 "in vec2 texCoord;\n"
950 "out vec4 oColor;\n"
951 "void main() {\n"
952 " oColor = texture(tex, texCoord);\n"
953 "}\n";
954
955 GLsizei width = 8;
956 GLsizei height = 8;
957
958 GLint maxDrawBuffers = 0;
959 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
960 // ES3 requires a minimum value of 4 for MAX_DRAW_BUFFERS.
961 ASSERT_GE(maxDrawBuffers, 2);
962
963 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
964 glUseProgram(program.get());
965 glViewport(0, 0, width, height);
966
967 GLTexture tex0;
968 GLTexture tex1;
969 GLFramebuffer fbo;
970 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
971 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
972 ASSERT_GL_NO_ERROR();
973
974 glBindTexture(GL_TEXTURE_2D, tex1.get());
975 GLint texLoc = glGetUniformLocation(program.get(), "tex");
976 ASSERT_NE(-1, texLoc);
977 glUniform1i(texLoc, 0);
978
979 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
980 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
981 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
982 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
983 ASSERT_GL_NO_ERROR();
984
985 drawBuffersFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}}, GL_INVALID_OPERATION);
986 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
987 GL_INVALID_OPERATION);
988 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
989}
990
Jamie Madill1d37bc52017-02-02 19:59:58 -0500991// This test covers detection of rendering feedback loops between the FBO and a depth Texture.
992// Based on WebGL test conformance2/rendering/depth-stencil-feedback-loop.html
993TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
994{
995 const std::string vertexShader =
996 "#version 300 es\n"
997 "in vec4 aPosition;\n"
998 "out vec2 texCoord;\n"
999 "void main() {\n"
1000 " gl_Position = aPosition;\n"
1001 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
1002 "}\n";
1003
1004 const std::string fragmentShader =
1005 "#version 300 es\n"
1006 "precision mediump float;\n"
1007 "uniform sampler2D tex;\n"
1008 "in vec2 texCoord;\n"
1009 "out vec4 oColor;\n"
1010 "void main() {\n"
1011 " oColor = texture(tex, texCoord);\n"
1012 "}\n";
1013
1014 GLsizei width = 8;
1015 GLsizei height = 8;
1016
1017 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
1018 glUseProgram(program.get());
1019
1020 glViewport(0, 0, width, height);
1021
1022 GLint texLoc = glGetUniformLocation(program.get(), "tex");
1023 glUniform1i(texLoc, 0);
1024
1025 // Create textures and allocate storage
1026 GLTexture tex0;
1027 GLTexture tex1;
1028 GLRenderbuffer rb;
1029 FillTexture2D(tex0.get(), width, height, GLColor::black, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
1030 FillTexture2D(tex1.get(), width, height, 0x80, 0, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT,
1031 GL_UNSIGNED_INT);
1032 glBindRenderbuffer(GL_RENDERBUFFER, rb.get());
1033 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
1034 ASSERT_GL_NO_ERROR();
1035
1036 GLFramebuffer fbo;
1037 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
1038 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
1039
1040 // Test rendering and sampling feedback loop for depth buffer
1041 glBindTexture(GL_TEXTURE_2D, tex1.get());
1042 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex1.get(), 0);
1043 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1044
1045 // The same image is used as depth buffer during rendering.
1046 glEnable(GL_DEPTH_TEST);
1047 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1048 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1049
1050 // The same image is used as depth buffer. But depth mask is false.
1051 glDepthMask(GL_FALSE);
1052 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1053 EXPECT_GL_NO_ERROR();
1054
1055 // The same image is used as depth buffer. But depth test is not enabled during rendering.
1056 glDepthMask(GL_TRUE);
1057 glDisable(GL_DEPTH_TEST);
1058 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1059 EXPECT_GL_NO_ERROR();
1060
1061 // Test rendering and sampling feedback loop for stencil buffer
1062 glBindTexture(GL_RENDERBUFFER, rb.get());
1063 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
1064 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb.get());
1065 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1066 constexpr GLint stencilClearValue = 0x40;
1067 glClearBufferiv(GL_STENCIL, 0, &stencilClearValue);
1068
1069 // The same image is used as stencil buffer during rendering.
1070 glEnable(GL_STENCIL_TEST);
1071 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1072 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1073
1074 // The same image is used as stencil buffer. But stencil mask is zero.
1075 glStencilMask(0x0);
1076 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1077 EXPECT_GL_NO_ERROR();
1078
1079 // The same image is used as stencil buffer. But stencil test is not enabled during rendering.
1080 glStencilMask(0xffff);
1081 glDisable(GL_STENCIL_TEST);
1082 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1083 EXPECT_GL_NO_ERROR();
1084}
1085
Jamie Madillfd3dd432017-02-02 19:59:59 -05001086// The source and the target for CopyTexSubImage3D are the same 3D texture.
1087// But the level of the 3D texture != the level of the read attachment.
1088TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLevels)
1089{
1090 GLTexture texture;
1091 GLFramebuffer framebuffer;
1092
1093 glBindTexture(GL_TEXTURE_3D, texture.get());
1094 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1095
1096 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1097 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1098 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
1099 ASSERT_GL_NO_ERROR();
1100
1101 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1102 EXPECT_GL_NO_ERROR();
1103}
1104
1105// The source and the target for CopyTexSubImage3D are the same 3D texture.
1106// But the zoffset of the 3D texture != the layer of the read attachment.
1107TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLayers)
1108{
1109 GLTexture texture;
1110 GLFramebuffer framebuffer;
1111
1112 glBindTexture(GL_TEXTURE_3D, texture.get());
1113 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1114
1115 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1116 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 1);
1117 ASSERT_GL_NO_ERROR();
1118
1119 glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
1120 EXPECT_GL_NO_ERROR();
1121}
1122
1123// The source and the target for CopyTexSubImage3D are the same 3D texture.
1124// And the level / zoffset of the 3D texture is equal to the level / layer of the read attachment.
1125TEST_P(WebGL2CompatibilityTest, TextureCopyingFeedbackLoop3D)
1126{
1127 GLTexture texture;
1128 GLFramebuffer framebuffer;
1129
1130 glBindTexture(GL_TEXTURE_3D, texture.get());
1131 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1132
1133 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1134 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1135 glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1136 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 1, 0);
1137 ASSERT_GL_NO_ERROR();
1138
1139 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1140 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1141}
1142
Geoff Langc287ea62016-09-16 14:46:51 -04001143// Use this to select which configurations (e.g. which renderer, which GLES major version) these
1144// tests should be run against.
1145ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
1146 ES2_D3D9(),
1147 ES2_D3D11(),
1148 ES3_D3D11(),
1149 ES2_D3D11_FL9_3(),
1150 ES2_OPENGL(),
1151 ES3_OPENGL(),
1152 ES2_OPENGLES(),
1153 ES3_OPENGLES());
1154
Jamie Madill07be8bf2017-02-02 19:59:57 -05001155ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
Geoff Langc287ea62016-09-16 14:46:51 -04001156} // namespace