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