blob: 0c9f1b180d1dcffec657d9aa00b55e532203ac64 [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
Corentin Wallezfd456442016-12-21 17:57:00 -0500426// Test the checks for OOB reads in the vertex buffers, instanced version
427TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
428{
429 const std::string &vert =
430 "attribute float a_pos;\n"
431 "void main()\n"
432 "{\n"
433 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
434 "}\n";
435
436 const std::string &frag =
437 "precision highp float;\n"
438 "void main()\n"
439 "{\n"
440 " gl_FragColor = vec4(1.0);\n"
441 "}\n";
442
443 ANGLE_GL_PROGRAM(program, vert, frag);
444
445 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
446 ASSERT_NE(-1, posLocation);
447 glUseProgram(program.get());
448
449 GLBuffer buffer;
450 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
451 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
452
453 glEnableVertexAttribArray(posLocation);
454 glVertexAttribDivisor(posLocation, 1);
455
456 const uint8_t* zeroOffset = nullptr;
457
458 // Test touching the last element is valid.
459 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
460 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
461 ASSERT_GL_NO_ERROR();
462
463 // Test touching the last element + 1 is invalid.
464 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
465 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
466 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
467
468 // Test touching the last element is valid, using a stride.
469 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
470 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
471 ASSERT_GL_NO_ERROR();
472
473 // Test touching the last element + 1 is invalid, using a stride.
474 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
475 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
476 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
477
478 // Test any offset is valid if no vertices are drawn.
479 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
480 glDrawArraysInstanced(GL_POINTS, 0, 1, 0);
481 ASSERT_GL_NO_ERROR();
482}
483
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500484// Tests that NPOT is not enabled by default in WebGL 1 and that it can be enabled
485TEST_P(WebGLCompatibilityTest, NPOT)
486{
487 EXPECT_FALSE(extensionEnabled("GL_OES_texture_npot"));
488
489 // Create a texture and set an NPOT mip 0, should always be acceptable.
490 GLTexture texture;
491 glBindTexture(GL_TEXTURE_2D, texture.get());
492 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
493 ASSERT_GL_NO_ERROR();
494
495 // Try setting an NPOT mip 1 and verify the error if WebGL 1
496 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
497 if (getClientMajorVersion() < 3)
498 {
499 ASSERT_GL_ERROR(GL_INVALID_VALUE);
500 }
501 else
502 {
503 ASSERT_GL_NO_ERROR();
504 }
505
506 if (extensionRequestable("GL_OES_texture_npot"))
507 {
508 glRequestExtensionANGLE("GL_OES_texture_npot");
509 ASSERT_GL_NO_ERROR();
510
511 // Try again to set NPOT mip 1
512 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
513 ASSERT_GL_NO_ERROR();
514 }
515}
516
Jamie Madillcad97ee2017-02-02 18:52:44 -0500517template <typename T>
518void FillTexture2D(GLuint texture,
519 GLsizei width,
520 GLsizei height,
521 const T &onePixelData,
522 GLint level,
523 GLint internalFormat,
524 GLenum format,
525 GLenum type)
526{
527 std::vector<T> allPixelsData(width * height, onePixelData);
528
529 glBindTexture(GL_TEXTURE_2D, texture);
530 glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, 0, format, type,
531 allPixelsData.data());
532 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
533 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
534 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
535 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
536}
537
Jamie Madilla4595b82017-01-11 17:36:34 -0500538// Tests that a rendering feedback loop triggers a GL error under WebGL.
539// Based on WebGL test conformance/renderbuffers/feedback-loop.html.
540TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoop)
541{
542 const std::string vertexShader =
543 "attribute vec4 a_position;\n"
544 "varying vec2 v_texCoord;\n"
545 "void main() {\n"
546 " gl_Position = a_position;\n"
547 " v_texCoord = (a_position.xy * 0.5) + 0.5;\n"
548 "}\n";
549
550 const std::string fragmentShader =
551 "precision mediump float;\n"
552 "varying vec2 v_texCoord;\n"
553 "uniform sampler2D u_texture;\n"
554 "void main() {\n"
555 " // Shader swizzles color channels so we can tell if the draw succeeded.\n"
556 " gl_FragColor = texture2D(u_texture, v_texCoord).gbra;\n"
557 "}\n";
558
559 GLTexture texture;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500560 FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500561
562 ASSERT_GL_NO_ERROR();
563
564 GLFramebuffer framebuffer;
565 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
566 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
567
568 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
569
570 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
571
572 GLint uniformLoc = glGetUniformLocation(program.get(), "u_texture");
573 ASSERT_NE(-1, uniformLoc);
574
575 glUseProgram(program.get());
576 glUniform1i(uniformLoc, 0);
577 glDisable(GL_BLEND);
578 glDisable(GL_DEPTH_TEST);
579 ASSERT_GL_NO_ERROR();
580
581 // Drawing with a texture that is also bound to the current framebuffer should fail
582 glBindTexture(GL_TEXTURE_2D, texture.get());
583 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
584 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
585
586 // Ensure that the texture contents did not change after the previous render
587 glBindFramebuffer(GL_FRAMEBUFFER, 0);
588 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
589 ASSERT_GL_NO_ERROR();
590 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
591
592 // Drawing when texture is bound to an inactive uniform should succeed
593 GLTexture texture2;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500594 FillTexture2D(texture2.get(), 1, 1, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500595
596 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
597 glActiveTexture(GL_TEXTURE1);
598 glBindTexture(GL_TEXTURE_2D, texture.get());
599 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
600 ASSERT_GL_NO_ERROR();
601 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
602}
603
Bryan Bernhart58806562017-01-05 13:09:31 -0800604// Test for the max draw buffers and color attachments.
605TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
606{
607 // This test only applies to ES2.
608 if (getClientMajorVersion() != 2)
609 {
610 return;
611 }
612
613 GLFramebuffer fbo[2];
614 glBindFramebuffer(GL_FRAMEBUFFER, fbo[0].get());
615
616 // Test that is valid when we bind with a single attachment point.
617 GLTexture texture;
618 glBindTexture(GL_TEXTURE_2D, texture.get());
619 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
620 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
621 ASSERT_GL_NO_ERROR();
622
623 // Test that enabling the draw buffers extension will allow us to bind with a non-zero
624 // attachment point.
625 if (extensionRequestable("GL_EXT_draw_buffers"))
626 {
627 glRequestExtensionANGLE("GL_EXT_draw_buffers");
628 EXPECT_GL_NO_ERROR();
629 EXPECT_TRUE(extensionEnabled("GL_EXT_draw_buffers"));
630
631 glBindFramebuffer(GL_FRAMEBUFFER, fbo[1].get());
632
633 GLTexture texture2;
634 glBindTexture(GL_TEXTURE_2D, texture2.get());
635 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
636 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2.get(),
637 0);
638 ASSERT_GL_NO_ERROR();
639 }
640}
641
Corentin Wallez3f6d4df2017-01-30 18:04:36 -0500642// Test that the offset in the index buffer is forced to be a multiple of the element size
643TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
644{
645 const std::string &vert =
646 "attribute vec3 a_pos;\n"
647 "void main()\n"
648 "{\n"
649 " gl_Position = vec4(a_pos, 1.0);\n"
650 "}\n";
651
652 const std::string &frag =
653 "precision highp float;\n"
654 "void main()\n"
655 "{\n"
656 " gl_FragColor = vec4(1.0);\n"
657 "}\n";
658
659 ANGLE_GL_PROGRAM(program, vert, frag);
660
661 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
662 ASSERT_NE(-1, posLocation);
663 glUseProgram(program.get());
664
665 const auto &vertices = GetQuadVertices();
666
667 GLBuffer vertexBuffer;
668 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
669 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
670 GL_STATIC_DRAW);
671
672 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
673 glEnableVertexAttribArray(posLocation);
674
675 GLBuffer indexBuffer;
676 const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0};
677 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
678 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
679
680 ASSERT_GL_NO_ERROR();
681
682 const char *zeroIndices = nullptr;
683
684 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
685 ASSERT_GL_NO_ERROR();
686
687 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
688 ASSERT_GL_NO_ERROR();
689
690 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
691 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
692}
693
694// Test that the offset and stride in the vertex buffer is forced to be a multiple of the element
695// size
696TEST_P(WebGLCompatibilityTest, VertexAttribPointerOffsetRestriction)
697{
698 const char *zeroOffset = nullptr;
699
700 // Base case, vector of two floats
701 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset);
702 ASSERT_GL_NO_ERROR();
703
704 // Test setting a non-multiple offset
705 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 1);
706 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
707 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 2);
708 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
709 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 3);
710 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
711
712 // Test setting a non-multiple stride
713 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 1, zeroOffset);
714 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
715 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, zeroOffset);
716 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
717 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3, zeroOffset);
718 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
719}
720
Jamie Madillcad97ee2017-02-02 18:52:44 -0500721void WebGLCompatibilityTest::drawBuffersEXTFeedbackLoop(GLuint program,
722 const std::array<GLenum, 2> &drawBuffers,
723 GLenum expectedError)
724{
725 glDrawBuffersEXT(2, drawBuffers.data());
726
727 // Make sure framebuffer is complete before feedback loop detection
728 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
729
730 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
731
732 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
733 // it should be NO_ERROR"
734 EXPECT_GL_ERROR(expectedError);
735}
736
737// This tests that rendering feedback loops works as expected with GL_EXT_draw_buffers.
738// Based on WebGL test conformance/extensions/webgl-draw-buffers-feedback-loop.html
739TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoopWithDrawBuffersEXT)
740{
741 const std::string vertexShader =
742 "attribute vec4 aPosition;\n"
743 "varying vec2 texCoord;\n"
744 "void main() {\n"
745 " gl_Position = aPosition;\n"
746 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
747 "}\n";
748
749 const std::string fragmentShader =
750 "#extension GL_EXT_draw_buffers : require\n"
751 "precision mediump float;\n"
752 "uniform sampler2D tex;\n"
753 "varying vec2 texCoord;\n"
754 "void main() {\n"
755 " gl_FragData[0] = texture2D(tex, texCoord);\n"
756 " gl_FragData[1] = texture2D(tex, texCoord);\n"
757 "}\n";
758
759 GLsizei width = 8;
760 GLsizei height = 8;
761
762 // This shader cannot be run in ES3, because WebGL 2 does not expose the draw buffers
763 // extension and gl_FragData semantics are changed to enforce indexing by zero always.
764 // TODO(jmadill): This extension should be disabled in WebGL 2 contexts.
765 if (/*!extensionEnabled("GL_EXT_draw_buffers")*/ getClientMajorVersion() != 2)
766 {
767 // No WEBGL_draw_buffers support -- this is legal.
768 return;
769 }
770
771 GLint maxDrawBuffers = 0;
772 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
773
774 if (maxDrawBuffers < 2)
775 {
776 std::cout << "Test skipped because MAX_DRAW_BUFFERS is too small." << std::endl;
777 return;
778 }
779
780 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
781 glUseProgram(program.get());
782 glViewport(0, 0, width, height);
783
784 GLTexture tex0;
785 GLTexture tex1;
786 GLFramebuffer fbo;
787 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
788 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
789 ASSERT_GL_NO_ERROR();
790
791 glBindTexture(GL_TEXTURE_2D, tex1.get());
792 GLint texLoc = glGetUniformLocation(program.get(), "tex");
793 ASSERT_NE(-1, texLoc);
794 glUniform1i(texLoc, 0);
795 ASSERT_GL_NO_ERROR();
796
797 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
798 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
799 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
800 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
801
802 drawBuffersEXTFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}},
803 GL_INVALID_OPERATION);
804 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
805 GL_INVALID_OPERATION);
806 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
807}
808
Jamie Madill07be8bf2017-02-02 19:59:57 -0500809// Test tests that texture copying feedback loops are properly rejected in WebGL.
810// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
811TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
812{
813 GLTexture texture;
814 glBindTexture(GL_TEXTURE_2D, texture.get());
815 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
816 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
817 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
820
821 GLTexture texture2;
822 glBindTexture(GL_TEXTURE_2D, texture2.get());
823 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
824 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
825 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
826 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
827 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
828
829 GLFramebuffer framebuffer;
830 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
831 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
832
833 // framebuffer should be FRAMEBUFFER_COMPLETE.
834 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
835 ASSERT_GL_NO_ERROR();
836
837 // testing copyTexImage2D
838
839 // copyTexImage2D to same texture but different level
840 glBindTexture(GL_TEXTURE_2D, texture.get());
841 glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
842 EXPECT_GL_NO_ERROR();
843
844 // copyTexImage2D to same texture same level, invalid feedback loop
845 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
846 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
847
848 // copyTexImage2D to different texture
849 glBindTexture(GL_TEXTURE_2D, texture2.get());
850 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
851 EXPECT_GL_NO_ERROR();
852
853 // testing copyTexSubImage2D
854
855 // copyTexSubImage2D to same texture but different level
856 glBindTexture(GL_TEXTURE_2D, texture.get());
857 glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
858 EXPECT_GL_NO_ERROR();
859
860 // copyTexSubImage2D to same texture same level, invalid feedback loop
861 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
862 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
863
864 // copyTexSubImage2D to different texture
865 glBindTexture(GL_TEXTURE_2D, texture2.get());
866 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
867 EXPECT_GL_NO_ERROR();
868}
869
870void WebGLCompatibilityTest::drawBuffersFeedbackLoop(GLuint program,
871 const std::array<GLenum, 2> &drawBuffers,
872 GLenum expectedError)
873{
874 glDrawBuffers(2, drawBuffers.data());
875
876 // Make sure framebuffer is complete before feedback loop detection
877 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
878
879 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
880
881 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
882 // it should be NO_ERROR"
883 EXPECT_GL_ERROR(expectedError);
884}
885
886// This tests that rendering feedback loops works as expected with WebGL 2.
887// Based on WebGL test conformance2/rendering/rendering-sampling-feedback-loop.html
888TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDrawBuffers)
889{
890 const std::string vertexShader =
891 "#version 300 es\n"
892 "in vec4 aPosition;\n"
893 "out vec2 texCoord;\n"
894 "void main() {\n"
895 " gl_Position = aPosition;\n"
896 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
897 "}\n";
898
899 const std::string fragmentShader =
900 "#version 300 es\n"
901 "precision mediump float;\n"
902 "uniform sampler2D tex;\n"
903 "in vec2 texCoord;\n"
904 "out vec4 oColor;\n"
905 "void main() {\n"
906 " oColor = texture(tex, texCoord);\n"
907 "}\n";
908
909 GLsizei width = 8;
910 GLsizei height = 8;
911
912 GLint maxDrawBuffers = 0;
913 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
914 // ES3 requires a minimum value of 4 for MAX_DRAW_BUFFERS.
915 ASSERT_GE(maxDrawBuffers, 2);
916
917 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
918 glUseProgram(program.get());
919 glViewport(0, 0, width, height);
920
921 GLTexture tex0;
922 GLTexture tex1;
923 GLFramebuffer fbo;
924 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
925 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
926 ASSERT_GL_NO_ERROR();
927
928 glBindTexture(GL_TEXTURE_2D, tex1.get());
929 GLint texLoc = glGetUniformLocation(program.get(), "tex");
930 ASSERT_NE(-1, texLoc);
931 glUniform1i(texLoc, 0);
932
933 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
934 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
935 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
936 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
937 ASSERT_GL_NO_ERROR();
938
939 drawBuffersFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}}, GL_INVALID_OPERATION);
940 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
941 GL_INVALID_OPERATION);
942 drawBuffersFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
943}
944
Jamie Madill1d37bc52017-02-02 19:59:58 -0500945// This test covers detection of rendering feedback loops between the FBO and a depth Texture.
946// Based on WebGL test conformance2/rendering/depth-stencil-feedback-loop.html
947TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
948{
949 const std::string vertexShader =
950 "#version 300 es\n"
951 "in vec4 aPosition;\n"
952 "out vec2 texCoord;\n"
953 "void main() {\n"
954 " gl_Position = aPosition;\n"
955 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
956 "}\n";
957
958 const std::string fragmentShader =
959 "#version 300 es\n"
960 "precision mediump float;\n"
961 "uniform sampler2D tex;\n"
962 "in vec2 texCoord;\n"
963 "out vec4 oColor;\n"
964 "void main() {\n"
965 " oColor = texture(tex, texCoord);\n"
966 "}\n";
967
968 GLsizei width = 8;
969 GLsizei height = 8;
970
971 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
972 glUseProgram(program.get());
973
974 glViewport(0, 0, width, height);
975
976 GLint texLoc = glGetUniformLocation(program.get(), "tex");
977 glUniform1i(texLoc, 0);
978
979 // Create textures and allocate storage
980 GLTexture tex0;
981 GLTexture tex1;
982 GLRenderbuffer rb;
983 FillTexture2D(tex0.get(), width, height, GLColor::black, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
984 FillTexture2D(tex1.get(), width, height, 0x80, 0, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT,
985 GL_UNSIGNED_INT);
986 glBindRenderbuffer(GL_RENDERBUFFER, rb.get());
987 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
988 ASSERT_GL_NO_ERROR();
989
990 GLFramebuffer fbo;
991 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
992 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
993
994 // Test rendering and sampling feedback loop for depth buffer
995 glBindTexture(GL_TEXTURE_2D, tex1.get());
996 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex1.get(), 0);
997 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
998
999 // The same image is used as depth buffer during rendering.
1000 glEnable(GL_DEPTH_TEST);
1001 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1002 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1003
1004 // The same image is used as depth buffer. But depth mask is false.
1005 glDepthMask(GL_FALSE);
1006 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1007 EXPECT_GL_NO_ERROR();
1008
1009 // The same image is used as depth buffer. But depth test is not enabled during rendering.
1010 glDepthMask(GL_TRUE);
1011 glDisable(GL_DEPTH_TEST);
1012 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1013 EXPECT_GL_NO_ERROR();
1014
1015 // Test rendering and sampling feedback loop for stencil buffer
1016 glBindTexture(GL_RENDERBUFFER, rb.get());
1017 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
1018 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rb.get());
1019 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
1020 constexpr GLint stencilClearValue = 0x40;
1021 glClearBufferiv(GL_STENCIL, 0, &stencilClearValue);
1022
1023 // The same image is used as stencil buffer during rendering.
1024 glEnable(GL_STENCIL_TEST);
1025 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1026 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1027
1028 // The same image is used as stencil buffer. But stencil mask is zero.
1029 glStencilMask(0x0);
1030 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1031 EXPECT_GL_NO_ERROR();
1032
1033 // The same image is used as stencil buffer. But stencil test is not enabled during rendering.
1034 glStencilMask(0xffff);
1035 glDisable(GL_STENCIL_TEST);
1036 drawQuad(program.get(), "aPosition", 0.5f, 1.0f, true);
1037 EXPECT_GL_NO_ERROR();
1038}
1039
Jamie Madillfd3dd432017-02-02 19:59:59 -05001040// The source and the target for CopyTexSubImage3D are the same 3D texture.
1041// But the level of the 3D texture != the level of the read attachment.
1042TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLevels)
1043{
1044 GLTexture texture;
1045 GLFramebuffer framebuffer;
1046
1047 glBindTexture(GL_TEXTURE_3D, texture.get());
1048 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1049
1050 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1051 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1052 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
1053 ASSERT_GL_NO_ERROR();
1054
1055 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1056 EXPECT_GL_NO_ERROR();
1057}
1058
1059// The source and the target for CopyTexSubImage3D are the same 3D texture.
1060// But the zoffset of the 3D texture != the layer of the read attachment.
1061TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLayers)
1062{
1063 GLTexture texture;
1064 GLFramebuffer framebuffer;
1065
1066 glBindTexture(GL_TEXTURE_3D, texture.get());
1067 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1068
1069 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1070 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 1);
1071 ASSERT_GL_NO_ERROR();
1072
1073 glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
1074 EXPECT_GL_NO_ERROR();
1075}
1076
1077// The source and the target for CopyTexSubImage3D are the same 3D texture.
1078// And the level / zoffset of the 3D texture is equal to the level / layer of the read attachment.
1079TEST_P(WebGL2CompatibilityTest, TextureCopyingFeedbackLoop3D)
1080{
1081 GLTexture texture;
1082 GLFramebuffer framebuffer;
1083
1084 glBindTexture(GL_TEXTURE_3D, texture.get());
1085 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
1086
1087 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1088 glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1089 glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1090 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 1, 0);
1091 ASSERT_GL_NO_ERROR();
1092
1093 glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
1094 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1095}
1096
Geoff Langc287ea62016-09-16 14:46:51 -04001097// Use this to select which configurations (e.g. which renderer, which GLES major version) these
1098// tests should be run against.
1099ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
1100 ES2_D3D9(),
1101 ES2_D3D11(),
1102 ES3_D3D11(),
1103 ES2_D3D11_FL9_3(),
1104 ES2_OPENGL(),
1105 ES3_OPENGL(),
1106 ES2_OPENGLES(),
1107 ES3_OPENGLES());
1108
Jamie Madill07be8bf2017-02-02 19:59:57 -05001109ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
Geoff Langc287ea62016-09-16 14:46:51 -04001110} // namespace