blob: 0964855643bb1a939c108efeeb1fcacd93382f83 [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
Geoff Langc339c4e2016-11-29 10:37:36 -050044 PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
Geoff Langc287ea62016-09-16 14:46:51 -040045};
46
Corentin Wallezfd456442016-12-21 17:57:00 -050047class WebGL2CompatibilityTest : public WebGLCompatibilityTest
48{
49};
50
Geoff Langc287ea62016-09-16 14:46:51 -040051// Context creation would fail if EGL_ANGLE_create_context_webgl_compatibility was not available so
52// the GL extension should always be present
53TEST_P(WebGLCompatibilityTest, ExtensionStringExposed)
54{
55 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
56}
57
58// Verify that all extension entry points are available
59TEST_P(WebGLCompatibilityTest, EntryPoints)
60{
Geoff Langc339c4e2016-11-29 10:37:36 -050061 if (extensionEnabled("GL_ANGLE_request_extension"))
Geoff Langc287ea62016-09-16 14:46:51 -040062 {
Geoff Langc339c4e2016-11-29 10:37:36 -050063 EXPECT_NE(nullptr, eglGetProcAddress("glRequestExtensionANGLE"));
Geoff Langc287ea62016-09-16 14:46:51 -040064 }
65}
66
67// WebGL 1 allows GL_DEPTH_STENCIL_ATTACHMENT as a valid binding point. Make sure it is usable,
68// even in ES2 contexts.
69TEST_P(WebGLCompatibilityTest, DepthStencilBindingPoint)
70{
71 GLRenderbuffer renderbuffer;
72 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
73 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
74
75 GLFramebuffer framebuffer;
76 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
77 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
78 renderbuffer.get());
79
80 EXPECT_GL_NO_ERROR();
81}
82
83// Test that attempting to enable an extension that doesn't exist generates GL_INVALID_OPERATION
84TEST_P(WebGLCompatibilityTest, EnableExtensionValidation)
85{
Geoff Langc339c4e2016-11-29 10:37:36 -050086 glRequestExtensionANGLE("invalid_extension_string");
Geoff Langc287ea62016-09-16 14:46:51 -040087 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
88}
89
90// Test enabling the GL_OES_element_index_uint extension
91TEST_P(WebGLCompatibilityTest, EnableExtensionUintIndices)
92{
93 if (getClientMajorVersion() != 2)
94 {
95 // This test only works on ES2 where uint indices are not available by default
96 return;
97 }
98
99 EXPECT_FALSE(extensionEnabled("GL_OES_element_index_uint"));
100
101 GLBuffer indexBuffer;
102 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
103
104 GLuint data[] = {0, 1, 2, 1, 3, 2};
105 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
106
107 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
108 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
109 glUseProgram(program.get());
110
111 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
112 EXPECT_GL_ERROR(GL_INVALID_ENUM);
113
Geoff Langc339c4e2016-11-29 10:37:36 -0500114 if (extensionRequestable("GL_OES_element_index_uint"))
Geoff Langc287ea62016-09-16 14:46:51 -0400115 {
Geoff Langc339c4e2016-11-29 10:37:36 -0500116 glRequestExtensionANGLE("GL_OES_element_index_uint");
Geoff Langc287ea62016-09-16 14:46:51 -0400117 EXPECT_GL_NO_ERROR();
118 EXPECT_TRUE(extensionEnabled("GL_OES_element_index_uint"));
119
120 glDrawElements(GL_TRIANGLES, 2, GL_UNSIGNED_INT, nullptr);
121 EXPECT_GL_NO_ERROR();
122 }
123}
124
Bryan Bernhart87c182e2016-11-02 11:23:22 -0700125// Verify that shaders are of a compatible spec when the extension is enabled.
126TEST_P(WebGLCompatibilityTest, ExtensionCompilerSpec)
127{
128 EXPECT_TRUE(extensionEnabled("GL_ANGLE_webgl_compatibility"));
129
130 // Use of reserved _webgl prefix should fail when the shader specification is for WebGL.
131 const std::string &vert =
132 "struct Foo {\n"
133 " int _webgl_bar;\n"
134 "};\n"
135 "void main()\n"
136 "{\n"
137 " Foo foo = Foo(1);\n"
138 "}";
139
140 // Default fragement shader.
141 const std::string &frag =
142 "void main()\n"
143 "{\n"
144 " gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n"
145 "}";
146
147 GLuint program = CompileProgram(vert, frag);
148 EXPECT_EQ(0u, program);
149 glDeleteProgram(program);
150}
151
Corentin Wallez327411e2016-12-09 11:09:17 -0500152// Test that client-side array buffers are forbidden in WebGL mode
153TEST_P(WebGLCompatibilityTest, ForbidsClientSideArrayBuffer)
154{
155 const std::string &vert =
156 "attribute vec3 a_pos;\n"
157 "void main()\n"
158 "{\n"
159 " gl_Position = vec4(a_pos, 1.0);\n"
160 "}\n";
161
162 const std::string &frag =
163 "precision highp float;\n"
164 "void main()\n"
165 "{\n"
166 " gl_FragColor = vec4(1.0);\n"
167 "}\n";
168
169 ANGLE_GL_PROGRAM(program, vert, frag);
170
171 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
172 ASSERT_NE(-1, posLocation);
173 glUseProgram(program.get());
174
175 const auto &vertices = GetQuadVertices();
Corentin Wallezfd456442016-12-21 17:57:00 -0500176 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 4, vertices.data());
Corentin Wallez327411e2016-12-09 11:09:17 -0500177 glEnableVertexAttribArray(posLocation);
178
179 ASSERT_GL_NO_ERROR();
180 glDrawArrays(GL_TRIANGLES, 0, 6);
181 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
182}
183
184// Test that client-side element array buffers are forbidden in WebGL mode
185TEST_P(WebGLCompatibilityTest, ForbidsClientSideElementBuffer)
186{
187 const std::string &vert =
188 "attribute vec3 a_pos;\n"
189 "void main()\n"
190 "{\n"
191 " gl_Position = vec4(a_pos, 1.0);\n"
192 "}\n";
193
194 const std::string &frag =
195 "precision highp float;\n"
196 "void main()\n"
197 "{\n"
198 " gl_FragColor = vec4(1.0);\n"
199 "}\n";
200
201 ANGLE_GL_PROGRAM(program, vert, frag);
202
203 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
204 ASSERT_NE(-1, posLocation);
205 glUseProgram(program.get());
206
207 const auto &vertices = GetQuadVertices();
208
209 GLBuffer vertexBuffer;
210 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
211 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
212 GL_STATIC_DRAW);
213
214 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
215 glEnableVertexAttribArray(posLocation);
216
217 const GLubyte indices[] = {0, 1, 2, 3, 4, 5};
218
219 ASSERT_GL_NO_ERROR();
220 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
221 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
222}
223
Corentin Wallezb1d0a2552016-12-19 16:15:54 -0500224// Tests the WebGL requirement of having the same stencil mask, writemask and ref for fron and back
225TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
226{
227 // Run the test in an FBO to make sure we have some stencil bits.
228 GLRenderbuffer renderbuffer;
229 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
230 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
231
232 GLFramebuffer framebuffer;
233 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
234 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
235 renderbuffer.get());
236
237 ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
238 "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
239 glUseProgram(program.get());
240 ASSERT_GL_NO_ERROR();
241
242 // Having ref and mask the same for front and back is valid.
243 glStencilMask(255);
244 glStencilFunc(GL_ALWAYS, 0, 255);
245 glDrawArrays(GL_TRIANGLES, 0, 6);
246 ASSERT_GL_NO_ERROR();
247
248 // Having a different front - back write mask generates an error.
249 glStencilMaskSeparate(GL_FRONT, 1);
250 glDrawArrays(GL_TRIANGLES, 0, 6);
251 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
252
253 // Setting both write masks separately to the same value is valid.
254 glStencilMaskSeparate(GL_BACK, 1);
255 glDrawArrays(GL_TRIANGLES, 0, 6);
256 ASSERT_GL_NO_ERROR();
257
258 // Having a different stencil front - back mask generates an error
259 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1);
260 glDrawArrays(GL_TRIANGLES, 0, 6);
261 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
262
263 // Setting both masks separately to the same value is valid.
264 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1);
265 glDrawArrays(GL_TRIANGLES, 0, 6);
266 ASSERT_GL_NO_ERROR();
267
268 // Having a different stencil front - back reference generates an error
269 glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1);
270 glDrawArrays(GL_TRIANGLES, 0, 6);
271 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
272
273 // Setting both references separately to the same value is valid.
274 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1);
275 glDrawArrays(GL_TRIANGLES, 0, 6);
276 ASSERT_GL_NO_ERROR();
277
278 // Using different stencil funcs, everything being equal is valid.
279 glStencilFuncSeparate(GL_BACK, GL_NEVER, 255, 1);
280 glDrawArrays(GL_TRIANGLES, 0, 6);
281 ASSERT_GL_NO_ERROR();
282}
283
Corentin Wallez506fc9c2016-12-21 16:53:33 -0500284// Test that GL_FIXED is forbidden
285TEST_P(WebGLCompatibilityTest, ForbidsGLFixed)
286{
287 GLBuffer buffer;
288 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
289 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
290
291 glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
292 ASSERT_GL_NO_ERROR();
293
294 glVertexAttribPointer(0, 1, GL_FIXED, GL_FALSE, 0, nullptr);
295 EXPECT_GL_ERROR(GL_INVALID_ENUM);
296}
297
298// Test the WebGL limit of 255 for the attribute stride
299TEST_P(WebGLCompatibilityTest, MaxStride)
300{
301 GLBuffer buffer;
302 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
303 glBufferData(GL_ARRAY_BUFFER, 1024, nullptr, GL_STATIC_DRAW);
304
305 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 255, nullptr);
306 ASSERT_GL_NO_ERROR();
307
308 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 256, nullptr);
309 EXPECT_GL_ERROR(GL_INVALID_VALUE);
310}
311
Corentin Wallezfd456442016-12-21 17:57:00 -0500312// Test the checks for OOB reads in the vertex buffers, non-instanced version
313TEST_P(WebGLCompatibilityTest, DrawArraysBufferOutOfBoundsNonInstanced)
314{
315 const std::string &vert =
316 "attribute float a_pos;\n"
317 "void main()\n"
318 "{\n"
319 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
320 "}\n";
321
322 const std::string &frag =
323 "precision highp float;\n"
324 "void main()\n"
325 "{\n"
326 " gl_FragColor = vec4(1.0);\n"
327 "}\n";
328
329 ANGLE_GL_PROGRAM(program, vert, frag);
330
331 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
332 ASSERT_NE(-1, posLocation);
333 glUseProgram(program.get());
334
335 GLBuffer buffer;
336 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
337 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
338
339 glEnableVertexAttribArray(posLocation);
340
341 const uint8_t* zeroOffset = nullptr;
342
343 // Test touching the last element is valid.
344 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
345 glDrawArrays(GL_POINTS, 0, 4);
346 ASSERT_GL_NO_ERROR();
347
348 // Test touching the last element + 1 is invalid.
349 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
350 glDrawArrays(GL_POINTS, 0, 4);
351 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
352
353 // Test touching the last element is valid, using a stride.
354 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
355 glDrawArrays(GL_POINTS, 0, 4);
356 ASSERT_GL_NO_ERROR();
357
358 // Test touching the last element + 1 is invalid, using a stride.
359 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
360 glDrawArrays(GL_POINTS, 0, 4);
361 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
362
363 // Test any offset is valid if no vertices are drawn.
364 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
365 glDrawArrays(GL_POINTS, 0, 0);
366 ASSERT_GL_NO_ERROR();
367}
368
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500369// Test the checks for OOB reads in the index buffer
370TEST_P(WebGLCompatibilityTest, DrawElementsBufferOutOfBoundsInIndexBuffer)
Geoff Lang5f319a42017-01-09 16:49:19 -0500371{
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500372 const std::string &vert =
373 "attribute float a_pos;\n"
374 "void main()\n"
375 "{\n"
376 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
377 "}\n";
Geoff Lang5f319a42017-01-09 16:49:19 -0500378
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500379 const std::string &frag =
380 "precision highp float;\n"
381 "void main()\n"
382 "{\n"
383 " gl_FragColor = vec4(1.0);\n"
384 "}\n";
385
386 ANGLE_GL_PROGRAM(program, vert, frag);
387
388 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
389 ASSERT_NE(-1, posLocation);
390 glUseProgram(program.get());
391
392 GLBuffer vertexBuffer;
393 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
394 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
395
396 glEnableVertexAttribArray(posLocation);
397
398 const uint8_t *zeroOffset = nullptr;
399 const uint8_t zeroIndices[] = {0, 0, 0, 0, 0, 0, 0, 0};
400
401 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset);
402
403 GLBuffer indexBuffer;
404 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
405 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(zeroIndices), zeroIndices, GL_STATIC_DRAW);
Geoff Lang5f319a42017-01-09 16:49:19 -0500406 ASSERT_GL_NO_ERROR();
407
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500408 // Test touching the last index is valid
409 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 4);
410 ASSERT_GL_NO_ERROR();
Geoff Lang5f319a42017-01-09 16:49:19 -0500411
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500412 // Test touching the last + 1 element is invalid
413 glDrawElements(GL_POINTS, 4, GL_UNSIGNED_BYTE, zeroOffset + 5);
414 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
Geoff Lang5f319a42017-01-09 16:49:19 -0500415
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500416 // Test any offset if valid if count is zero
417 glDrawElements(GL_POINTS, 0, GL_UNSIGNED_BYTE, zeroOffset + 42);
418 ASSERT_GL_NO_ERROR();
Geoff Lang5f319a42017-01-09 16:49:19 -0500419}
420
Corentin Wallezfd456442016-12-21 17:57:00 -0500421// Test the checks for OOB reads in the vertex buffers, instanced version
422TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
423{
424 const std::string &vert =
425 "attribute float a_pos;\n"
426 "void main()\n"
427 "{\n"
428 " gl_Position = vec4(a_pos, a_pos, a_pos, 1.0);\n"
429 "}\n";
430
431 const std::string &frag =
432 "precision highp float;\n"
433 "void main()\n"
434 "{\n"
435 " gl_FragColor = vec4(1.0);\n"
436 "}\n";
437
438 ANGLE_GL_PROGRAM(program, vert, frag);
439
440 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
441 ASSERT_NE(-1, posLocation);
442 glUseProgram(program.get());
443
444 GLBuffer buffer;
445 glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
446 glBufferData(GL_ARRAY_BUFFER, 16, nullptr, GL_STATIC_DRAW);
447
448 glEnableVertexAttribArray(posLocation);
449 glVertexAttribDivisor(posLocation, 1);
450
451 const uint8_t* zeroOffset = nullptr;
452
453 // Test touching the last element is valid.
454 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 12);
455 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
456 ASSERT_GL_NO_ERROR();
457
458 // Test touching the last element + 1 is invalid.
459 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 13);
460 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
461 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
462
463 // Test touching the last element is valid, using a stride.
464 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 9);
465 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
466 ASSERT_GL_NO_ERROR();
467
468 // Test touching the last element + 1 is invalid, using a stride.
469 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 2, zeroOffset + 10);
470 glDrawArraysInstanced(GL_POINTS, 0, 1, 4);
471 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
472
473 // Test any offset is valid if no vertices are drawn.
474 glVertexAttribPointer(0, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, zeroOffset + 32);
475 glDrawArraysInstanced(GL_POINTS, 0, 1, 0);
476 ASSERT_GL_NO_ERROR();
477}
478
Corentin Wallez0844f2d2017-01-31 17:02:59 -0500479// Tests that NPOT is not enabled by default in WebGL 1 and that it can be enabled
480TEST_P(WebGLCompatibilityTest, NPOT)
481{
482 EXPECT_FALSE(extensionEnabled("GL_OES_texture_npot"));
483
484 // Create a texture and set an NPOT mip 0, should always be acceptable.
485 GLTexture texture;
486 glBindTexture(GL_TEXTURE_2D, texture.get());
487 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
488 ASSERT_GL_NO_ERROR();
489
490 // Try setting an NPOT mip 1 and verify the error if WebGL 1
491 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
492 if (getClientMajorVersion() < 3)
493 {
494 ASSERT_GL_ERROR(GL_INVALID_VALUE);
495 }
496 else
497 {
498 ASSERT_GL_NO_ERROR();
499 }
500
501 if (extensionRequestable("GL_OES_texture_npot"))
502 {
503 glRequestExtensionANGLE("GL_OES_texture_npot");
504 ASSERT_GL_NO_ERROR();
505
506 // Try again to set NPOT mip 1
507 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 5, 5, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
508 ASSERT_GL_NO_ERROR();
509 }
510}
511
Jamie Madillcad97ee2017-02-02 18:52:44 -0500512template <typename T>
513void FillTexture2D(GLuint texture,
514 GLsizei width,
515 GLsizei height,
516 const T &onePixelData,
517 GLint level,
518 GLint internalFormat,
519 GLenum format,
520 GLenum type)
521{
522 std::vector<T> allPixelsData(width * height, onePixelData);
523
524 glBindTexture(GL_TEXTURE_2D, texture);
525 glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, 0, format, type,
526 allPixelsData.data());
527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
528 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
529 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
530 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
531}
532
Jamie Madilla4595b82017-01-11 17:36:34 -0500533// Tests that a rendering feedback loop triggers a GL error under WebGL.
534// Based on WebGL test conformance/renderbuffers/feedback-loop.html.
535TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoop)
536{
537 const std::string vertexShader =
538 "attribute vec4 a_position;\n"
539 "varying vec2 v_texCoord;\n"
540 "void main() {\n"
541 " gl_Position = a_position;\n"
542 " v_texCoord = (a_position.xy * 0.5) + 0.5;\n"
543 "}\n";
544
545 const std::string fragmentShader =
546 "precision mediump float;\n"
547 "varying vec2 v_texCoord;\n"
548 "uniform sampler2D u_texture;\n"
549 "void main() {\n"
550 " // Shader swizzles color channels so we can tell if the draw succeeded.\n"
551 " gl_FragColor = texture2D(u_texture, v_texCoord).gbra;\n"
552 "}\n";
553
554 GLTexture texture;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500555 FillTexture2D(texture.get(), 1, 1, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500556
557 ASSERT_GL_NO_ERROR();
558
559 GLFramebuffer framebuffer;
560 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
561 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
562
563 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
564
565 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
566
567 GLint uniformLoc = glGetUniformLocation(program.get(), "u_texture");
568 ASSERT_NE(-1, uniformLoc);
569
570 glUseProgram(program.get());
571 glUniform1i(uniformLoc, 0);
572 glDisable(GL_BLEND);
573 glDisable(GL_DEPTH_TEST);
574 ASSERT_GL_NO_ERROR();
575
576 // Drawing with a texture that is also bound to the current framebuffer should fail
577 glBindTexture(GL_TEXTURE_2D, texture.get());
578 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
579 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
580
581 // Ensure that the texture contents did not change after the previous render
582 glBindFramebuffer(GL_FRAMEBUFFER, 0);
583 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
584 ASSERT_GL_NO_ERROR();
585 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
586
587 // Drawing when texture is bound to an inactive uniform should succeed
588 GLTexture texture2;
Jamie Madillcad97ee2017-02-02 18:52:44 -0500589 FillTexture2D(texture2.get(), 1, 1, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
Jamie Madilla4595b82017-01-11 17:36:34 -0500590
591 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
592 glActiveTexture(GL_TEXTURE1);
593 glBindTexture(GL_TEXTURE_2D, texture.get());
594 drawQuad(program.get(), "a_position", 0.5f, 1.0f, true);
595 ASSERT_GL_NO_ERROR();
596 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
597}
598
Jamie Madillf695a3a2017-01-11 17:36:35 -0500599// Test tests that texture copying feedback loops are properly rejected in WebGL.
600// Based on the WebGL test conformance/textures/misc/texture-copying-feedback-loops.html
601TEST_P(WebGLCompatibilityTest, TextureCopyingFeedbackLoops)
602{
603 GLTexture texture;
604 glBindTexture(GL_TEXTURE_2D, texture.get());
605 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
606 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
607 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
608 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
609 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
610
611 GLTexture texture2;
612 glBindTexture(GL_TEXTURE_2D, texture2.get());
613 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
614 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
615 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
616 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
617 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
618
619 GLFramebuffer framebuffer;
620 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
621 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
622
623 // framebuffer should be FRAMEBUFFER_COMPLETE.
624 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
625 ASSERT_GL_NO_ERROR();
626
627 // testing copyTexImage2D
628
629 // copyTexImage2D to same texture but different level
630 glBindTexture(GL_TEXTURE_2D, texture.get());
631 glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
632 EXPECT_GL_NO_ERROR();
633
634 // copyTexImage2D to same texture same level, invalid feedback loop
635 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
636 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
637
638 // copyTexImage2D to different texture
639 glBindTexture(GL_TEXTURE_2D, texture2.get());
640 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 2, 2, 0);
641 EXPECT_GL_NO_ERROR();
642
643 // testing copyTexSubImage2D
644
645 // copyTexSubImage2D to same texture but different level
646 glBindTexture(GL_TEXTURE_2D, texture.get());
647 glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
648 EXPECT_GL_NO_ERROR();
649
650 // copyTexSubImage2D to same texture same level, invalid feedback loop
651 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
652 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
653
654 // copyTexSubImage2D to different texture
655 glBindTexture(GL_TEXTURE_2D, texture2.get());
656 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
657 EXPECT_GL_NO_ERROR();
658}
659
Bryan Bernhart58806562017-01-05 13:09:31 -0800660// Test for the max draw buffers and color attachments.
661TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
662{
663 // This test only applies to ES2.
664 if (getClientMajorVersion() != 2)
665 {
666 return;
667 }
668
669 GLFramebuffer fbo[2];
670 glBindFramebuffer(GL_FRAMEBUFFER, fbo[0].get());
671
672 // Test that is valid when we bind with a single attachment point.
673 GLTexture texture;
674 glBindTexture(GL_TEXTURE_2D, texture.get());
675 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
676 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
677 ASSERT_GL_NO_ERROR();
678
679 // Test that enabling the draw buffers extension will allow us to bind with a non-zero
680 // attachment point.
681 if (extensionRequestable("GL_EXT_draw_buffers"))
682 {
683 glRequestExtensionANGLE("GL_EXT_draw_buffers");
684 EXPECT_GL_NO_ERROR();
685 EXPECT_TRUE(extensionEnabled("GL_EXT_draw_buffers"));
686
687 glBindFramebuffer(GL_FRAMEBUFFER, fbo[1].get());
688
689 GLTexture texture2;
690 glBindTexture(GL_TEXTURE_2D, texture2.get());
691 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
692 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2.get(),
693 0);
694 ASSERT_GL_NO_ERROR();
695 }
696}
697
Corentin Wallez3f6d4df2017-01-30 18:04:36 -0500698// Test that the offset in the index buffer is forced to be a multiple of the element size
699TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
700{
701 const std::string &vert =
702 "attribute vec3 a_pos;\n"
703 "void main()\n"
704 "{\n"
705 " gl_Position = vec4(a_pos, 1.0);\n"
706 "}\n";
707
708 const std::string &frag =
709 "precision highp float;\n"
710 "void main()\n"
711 "{\n"
712 " gl_FragColor = vec4(1.0);\n"
713 "}\n";
714
715 ANGLE_GL_PROGRAM(program, vert, frag);
716
717 GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
718 ASSERT_NE(-1, posLocation);
719 glUseProgram(program.get());
720
721 const auto &vertices = GetQuadVertices();
722
723 GLBuffer vertexBuffer;
724 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
725 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
726 GL_STATIC_DRAW);
727
728 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
729 glEnableVertexAttribArray(posLocation);
730
731 GLBuffer indexBuffer;
732 const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0};
733 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
734 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
735
736 ASSERT_GL_NO_ERROR();
737
738 const char *zeroIndices = nullptr;
739
740 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
741 ASSERT_GL_NO_ERROR();
742
743 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
744 ASSERT_GL_NO_ERROR();
745
746 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
747 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
748}
749
750// Test that the offset and stride in the vertex buffer is forced to be a multiple of the element
751// size
752TEST_P(WebGLCompatibilityTest, VertexAttribPointerOffsetRestriction)
753{
754 const char *zeroOffset = nullptr;
755
756 // Base case, vector of two floats
757 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset);
758 ASSERT_GL_NO_ERROR();
759
760 // Test setting a non-multiple offset
761 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 1);
762 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
763 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 2);
764 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
765 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 3);
766 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
767
768 // Test setting a non-multiple stride
769 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 1, zeroOffset);
770 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
771 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, zeroOffset);
772 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
773 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3, zeroOffset);
774 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
775}
776
Jamie Madillcad97ee2017-02-02 18:52:44 -0500777void WebGLCompatibilityTest::drawBuffersEXTFeedbackLoop(GLuint program,
778 const std::array<GLenum, 2> &drawBuffers,
779 GLenum expectedError)
780{
781 glDrawBuffersEXT(2, drawBuffers.data());
782
783 // Make sure framebuffer is complete before feedback loop detection
784 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
785
786 drawQuad(program, "aPosition", 0.5f, 1.0f, true);
787
788 // "Rendering to a texture where it samples from should geneates INVALID_OPERATION. Otherwise,
789 // it should be NO_ERROR"
790 EXPECT_GL_ERROR(expectedError);
791}
792
793// This tests that rendering feedback loops works as expected with GL_EXT_draw_buffers.
794// Based on WebGL test conformance/extensions/webgl-draw-buffers-feedback-loop.html
795TEST_P(WebGLCompatibilityTest, RenderingFeedbackLoopWithDrawBuffersEXT)
796{
797 const std::string vertexShader =
798 "attribute vec4 aPosition;\n"
799 "varying vec2 texCoord;\n"
800 "void main() {\n"
801 " gl_Position = aPosition;\n"
802 " texCoord = (aPosition.xy * 0.5) + 0.5;\n"
803 "}\n";
804
805 const std::string fragmentShader =
806 "#extension GL_EXT_draw_buffers : require\n"
807 "precision mediump float;\n"
808 "uniform sampler2D tex;\n"
809 "varying vec2 texCoord;\n"
810 "void main() {\n"
811 " gl_FragData[0] = texture2D(tex, texCoord);\n"
812 " gl_FragData[1] = texture2D(tex, texCoord);\n"
813 "}\n";
814
815 GLsizei width = 8;
816 GLsizei height = 8;
817
818 // This shader cannot be run in ES3, because WebGL 2 does not expose the draw buffers
819 // extension and gl_FragData semantics are changed to enforce indexing by zero always.
820 // TODO(jmadill): This extension should be disabled in WebGL 2 contexts.
821 if (/*!extensionEnabled("GL_EXT_draw_buffers")*/ getClientMajorVersion() != 2)
822 {
823 // No WEBGL_draw_buffers support -- this is legal.
824 return;
825 }
826
827 GLint maxDrawBuffers = 0;
828 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
829
830 if (maxDrawBuffers < 2)
831 {
832 std::cout << "Test skipped because MAX_DRAW_BUFFERS is too small." << std::endl;
833 return;
834 }
835
836 ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
837 glUseProgram(program.get());
838 glViewport(0, 0, width, height);
839
840 GLTexture tex0;
841 GLTexture tex1;
842 GLFramebuffer fbo;
843 FillTexture2D(tex0.get(), width, height, GLColor::red, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
844 FillTexture2D(tex1.get(), width, height, GLColor::green, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
845 ASSERT_GL_NO_ERROR();
846
847 glBindTexture(GL_TEXTURE_2D, tex1.get());
848 GLint texLoc = glGetUniformLocation(program.get(), "tex");
849 ASSERT_NE(-1, texLoc);
850 glUniform1i(texLoc, 0);
851 ASSERT_GL_NO_ERROR();
852
853 // The sampling texture is bound to COLOR_ATTACHMENT1 during resource allocation
854 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
855 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0.get(), 0);
856 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex1.get(), 0);
857
858 drawBuffersEXTFeedbackLoop(program.get(), {{GL_NONE, GL_COLOR_ATTACHMENT1}},
859 GL_INVALID_OPERATION);
860 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}},
861 GL_INVALID_OPERATION);
862 drawBuffersEXTFeedbackLoop(program.get(), {{GL_COLOR_ATTACHMENT0, GL_NONE}}, GL_NO_ERROR);
863}
864
Geoff Langc287ea62016-09-16 14:46:51 -0400865// Use this to select which configurations (e.g. which renderer, which GLES major version) these
866// tests should be run against.
867ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
868 ES2_D3D9(),
869 ES2_D3D11(),
870 ES3_D3D11(),
871 ES2_D3D11_FL9_3(),
872 ES2_OPENGL(),
873 ES3_OPENGL(),
874 ES2_OPENGLES(),
875 ES3_OPENGLES());
876
Corentin Wallezfd456442016-12-21 17:57:00 -0500877ANGLE_INSTANTIATE_TEST(WebGL2CompatibilityTest,
878 ES3_D3D11(),
879 ES3_OPENGL(),
880 ES3_OPENGLES());
881
Geoff Langc287ea62016-09-16 14:46:51 -0400882} // namespace