1 //
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 // Some of the pointsprite tests below were ported from Khronos WebGL
7 // conformance test suite.
8
9 #include "test_utils/ANGLETest.h"
10
11 #include <cmath>
12
13 using namespace angle;
14
15 class PointSpritesTest : public ANGLETest
16 {
17 protected:
18 const int windowWidth = 256;
19 const int windowHeight = 256;
PointSpritesTest()20 PointSpritesTest()
21 {
22 setWindowWidth(windowWidth);
23 setWindowHeight(windowHeight);
24 setConfigRedBits(8);
25 setConfigGreenBits(8);
26 setConfigBlueBits(8);
27 setConfigAlphaBits(8);
28 }
29
SetUp()30 virtual void SetUp()
31 {
32 ANGLETest::SetUp();
33 }
34
s2p(float s)35 float s2p(float s)
36 {
37 return (s + 1.0f) * 0.5f * (GLfloat)windowWidth;
38 }
39 };
40
41 // Checks gl_PointCoord and gl_PointSize
42 // https://www.khronos.org/registry/webgl/sdk/tests/conformance/glsl/variables/gl-pointcoord.html
TEST_P(PointSpritesTest,PointCoordAndPointSizeCompliance)43 TEST_P(PointSpritesTest, PointCoordAndPointSizeCompliance)
44 {
45 // TODO(jmadill): figure out why this fails
46 if (IsIntel() && GetParam() == ES2_D3D9())
47 {
48 std::cout << "Test skipped on Intel due to failures." << std::endl;
49 return;
50 }
51
52 const std::string fs = SHADER_SOURCE
53 (
54 precision mediump float;
55 void main()
56 {
57 gl_FragColor = vec4(
58 gl_PointCoord.x,
59 gl_PointCoord.y,
60 0,
61 1);
62 }
63 );
64
65 const std::string vs = SHADER_SOURCE
66 (
67 attribute vec4 vPosition;
68 uniform float uPointSize;
69 void main()
70 {
71 gl_PointSize = uPointSize;
72 gl_Position = vPosition;
73 }
74 );
75
76 GLuint program = CompileProgram(vs, fs);
77 ASSERT_NE(program, 0u);
78 ASSERT_GL_NO_ERROR();
79
80 glUseProgram(program);
81
82 GLfloat pointSizeRange[2] = {};
83 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
84
85 GLfloat maxPointSize = pointSizeRange[1];
86
87 ASSERT_TRUE(maxPointSize >= 1);
88 maxPointSize = floorf(maxPointSize);
89 ASSERT_TRUE((int)maxPointSize % 1 == 0);
90
91 maxPointSize = std::min(maxPointSize, 64.0f);
92 GLfloat pointWidth = maxPointSize / windowWidth;
93 GLint step = static_cast<GLint>(floorf(maxPointSize / 4));
94 GLint pointStep = std::max<GLint>(1, step);
95
96 GLint pointSizeLoc = glGetUniformLocation(program, "uPointSize");
97 ASSERT_GL_NO_ERROR();
98
99 glUniform1f(pointSizeLoc, maxPointSize);
100 ASSERT_GL_NO_ERROR();
101
102 GLfloat pixelOffset = ((int)maxPointSize % 2) ? (1.0f / (GLfloat)windowWidth) : 0;
103 GLuint vertexObject = 0;
104 glGenBuffers(1, &vertexObject);
105 ASSERT_NE(vertexObject, 0U);
106 ASSERT_GL_NO_ERROR();
107
108 glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
109 ASSERT_GL_NO_ERROR();
110
111 GLfloat thePoints[] = { -0.5f + pixelOffset, -0.5f + pixelOffset,
112 0.5f + pixelOffset, -0.5f + pixelOffset,
113 -0.5f + pixelOffset, 0.5f + pixelOffset,
114 0.5f + pixelOffset, 0.5f + pixelOffset };
115
116 glBufferData(GL_ARRAY_BUFFER, sizeof(thePoints), thePoints, GL_STATIC_DRAW);
117 ASSERT_GL_NO_ERROR();
118
119 glEnableVertexAttribArray(0);
120 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
121
122 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
123
124 glDrawArrays(GL_POINTS, 0, 4);
125 ASSERT_GL_NO_ERROR();
126
127 glDeleteBuffers(1, &vertexObject);
128
129 std::string debugText;
130 for (float py = 0; py < 2; ++py) {
131 for (float px = 0; px < 2; ++px) {
132 float pointX = -0.5f + px + pixelOffset;
133 float pointY = -0.5f + py + pixelOffset;
134 for (int yy = 0; yy < maxPointSize; yy += pointStep) {
135 for (int xx = 0; xx < maxPointSize; xx += pointStep) {
136 // formula for s and t from OpenGL ES 2.0 spec section 3.3
137 float xw = s2p(pointX);
138 float yw = s2p(pointY);
139 float u = xx / maxPointSize * 2 - 1;
140 float v = yy / maxPointSize * 2 - 1;
141 int xf = static_cast<int>(floorf(s2p(pointX + u * pointWidth)));
142 int yf = static_cast<int>(floorf(s2p(pointY + v * pointWidth)));
143 float s = 0.5f + (xf + 0.5f - xw) / maxPointSize;
144 float t = 0.5f + (yf + 0.5f - yw) / maxPointSize;
145 GLubyte color[4] = { static_cast<GLubyte>(floorf(s * 255)), static_cast<GLubyte>(floorf((1 - t) * 255)), 0, 255 };
146 EXPECT_PIXEL_NEAR(xf, yf, color[0], color[1], color[2], color[3], 4);
147 }
148 }
149 }
150 }
151 }
152
153 // Verify that drawing a point without enabling any attributes succeeds
154 // https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-no-attributes.html
TEST_P(PointSpritesTest,PointWithoutAttributesCompliance)155 TEST_P(PointSpritesTest, PointWithoutAttributesCompliance)
156 {
157 // clang-format off
158 const std::string fs = SHADER_SOURCE
159 (
160 precision mediump float;
161 void main()
162 {
163 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
164 }
165 );
166
167 const std::string vs = SHADER_SOURCE
168 (
169 void main()
170 {
171 gl_PointSize = 2.0;
172 gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
173 }
174 );
175 // clang-format on
176
177 GLuint program = CompileProgram(vs, fs);
178 ASSERT_NE(program, 0u);
179 ASSERT_GL_NO_ERROR();
180
181 glUseProgram(program);
182
183 glDrawArrays(GL_POINTS, 0, 1);
184 ASSERT_GL_NO_ERROR();
185
186 // expect the center pixel to be green
187 EXPECT_PIXEL_EQ((windowWidth - 1) / 2, (windowHeight - 1) / 2, 0, 255, 0, 255);
188 }
189
190 // This is a regression test for a graphics driver bug affecting end caps on roads in MapsGL
191 // https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-with-gl-pointcoord-in-fragment-shader.html
TEST_P(PointSpritesTest,PointCoordRegressionTest)192 TEST_P(PointSpritesTest, PointCoordRegressionTest)
193 {
194 const std::string fs = SHADER_SOURCE
195 (
196 precision mediump float;
197 varying vec4 v_color;
198 void main()
199 {
200 // It seems as long as this mathematical expression references
201 // gl_PointCoord, the fragment's color is incorrect.
202 vec2 diff = gl_PointCoord - vec2(.5, .5);
203 if (length(diff) > 0.5)
204 discard;
205
206 // The point should be a solid color.
207 gl_FragColor = v_color;
208 }
209 );
210
211 const std::string vs = SHADER_SOURCE
212 (
213 varying vec4 v_color;
214 // The X and Y coordinates of the center of the point.
215 attribute vec2 a_vertex;
216 uniform float u_pointSize;
217 void main()
218 {
219 gl_PointSize = u_pointSize;
220 gl_Position = vec4(a_vertex, 0.0, 1.0);
221 // The color of the point.
222 v_color = vec4(0.0, 1.0, 0.0, 1.0);
223 }
224 );
225
226 GLuint program = CompileProgram(vs, fs);
227 ASSERT_NE(program, 0u);
228 ASSERT_GL_NO_ERROR();
229
230 glUseProgram(program);
231
232 GLfloat pointSizeRange[2] = {};
233 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
234
235 GLfloat maxPointSize = pointSizeRange[1];
236
237 ASSERT_TRUE(maxPointSize > 2);
238
239 glClearColor(0, 0, 0, 1);
240 glDisable(GL_DEPTH_TEST);
241 glClear(GL_COLOR_BUFFER_BIT);
242
243 GLint pointSizeLoc = glGetUniformLocation(program, "u_pointSize");
244 ASSERT_GL_NO_ERROR();
245
246 GLfloat pointSize = std::min<GLfloat>(20.0f, maxPointSize);
247 glUniform1f(pointSizeLoc, pointSize);
248 ASSERT_GL_NO_ERROR();
249
250 GLuint vertexObject = 0;
251 glGenBuffers(1, &vertexObject);
252 ASSERT_NE(vertexObject, 0U);
253 ASSERT_GL_NO_ERROR();
254
255 glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
256 ASSERT_GL_NO_ERROR();
257
258 GLfloat thePoints[] = { 0.0f, 0.0f };
259
260 glBufferData(GL_ARRAY_BUFFER, sizeof(thePoints), thePoints, GL_STATIC_DRAW);
261 ASSERT_GL_NO_ERROR();
262
263 glEnableVertexAttribArray(0);
264 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
265
266 glDrawArrays(GL_POINTS, 0, 1);
267 ASSERT_GL_NO_ERROR();
268
269 // expect the center pixel to be green
270 EXPECT_PIXEL_EQ((windowWidth - 1) / 2, (windowHeight - 1) / 2, 0, 255, 0, 255);
271
272 glDeleteBuffers(1, &vertexObject);
273 }
274
275 // Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled
276 // https://www.khronos.org/registry/webgl/sdk/tests/conformance/rendering/point-size.html
TEST_P(PointSpritesTest,PointSizeEnabledCompliance)277 TEST_P(PointSpritesTest, PointSizeEnabledCompliance)
278 {
279 const std::string fs = SHADER_SOURCE
280 (
281 precision mediump float;
282 varying vec4 color;
283
284 void main()
285 {
286 gl_FragColor = color;
287 }
288 );
289
290 const std::string vs = SHADER_SOURCE
291 (
292 attribute vec3 pos;
293 attribute vec4 colorIn;
294 uniform float pointSize;
295 varying vec4 color;
296
297 void main()
298 {
299 gl_PointSize = pointSize;
300 color = colorIn;
301 gl_Position = vec4(pos, 1.0);
302 }
303 );
304
305 // The WebGL test is drawn on a 2x2 canvas. Emulate this by setting a 2x2 viewport.
306 glViewport(0, 0, 2, 2);
307
308 GLuint program = CompileProgram(vs, fs);
309 ASSERT_NE(program, 0u);
310 ASSERT_GL_NO_ERROR();
311
312 glUseProgram(program);
313
314 glDisable(GL_BLEND);
315
316 // The choice of (0.4, 0.4) ensures that the centers of the surrounding
317 // pixels are not contained within the point when it is of size 1, but
318 // that they definitely are when it is of size 2.
319 GLfloat vertices[] = { 0.4f, 0.4f, 0.0f };
320 GLubyte colors[] = { 255, 0, 0, 255 };
321
322 GLuint vertexObject = 0;
323 glGenBuffers(1, &vertexObject);
324 ASSERT_NE(vertexObject, 0U);
325 ASSERT_GL_NO_ERROR();
326
327 glBindBuffer(GL_ARRAY_BUFFER, vertexObject);
328 ASSERT_GL_NO_ERROR();
329
330 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), NULL, GL_STATIC_DRAW);
331 ASSERT_GL_NO_ERROR();
332
333 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
334 ASSERT_GL_NO_ERROR();
335
336 glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);
337 ASSERT_GL_NO_ERROR();
338
339 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
340
341 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
342 glEnableVertexAttribArray(0);
343
344 glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, (GLvoid*)sizeof(vertices));
345 glEnableVertexAttribArray(1);
346
347 GLint pointSizeLoc = glGetUniformLocation(program, "pointSize");
348 ASSERT_GL_NO_ERROR();
349
350 glUniform1f(pointSizeLoc, 1.0f);
351 ASSERT_GL_NO_ERROR();
352
353 glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(ArraySize(vertices)) / 3);
354 ASSERT_GL_NO_ERROR();
355
356 // Test the pixels around the target Red pixel to ensure
357 // they are the expected color values
358 for (GLint y = 0; y < 2; ++y)
359 {
360 for (GLint x = 0; x < 2; ++x)
361 {
362 // 1x1 is expected to be a red pixel
363 // All others are black
364 GLubyte expectedColor[4] = { 0, 0, 0, 0 };
365 if (x == 1 && y == 1)
366 {
367 expectedColor[0] = 255;
368 expectedColor[3] = 255;
369 }
370 EXPECT_PIXEL_EQ(x, y, expectedColor[0], expectedColor[1], expectedColor[2], expectedColor[3]);
371 }
372 }
373
374 GLfloat pointSizeRange[2] = {};
375 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
376
377 if (pointSizeRange[1] >= 2.0)
378 {
379 // Draw a point of size 2 and verify it fills the appropriate region.
380 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
381
382 glUniform1f(pointSizeLoc, 2.0f);
383 ASSERT_GL_NO_ERROR();
384
385 glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(ArraySize(vertices)) / 3);
386 ASSERT_GL_NO_ERROR();
387
388 // Test the pixels to ensure the target is ALL Red pixels
389 for (GLint y = 0; y < 2; ++y)
390 {
391 for (GLint x = 0; x < 2; ++x)
392 {
393 EXPECT_PIXEL_EQ(x, y, 255, 0, 0, 255);
394 }
395 }
396 }
397
398 glDeleteBuffers(1, &vertexObject);
399 }
400
401 // Verify that rendering works correctly when gl_PointSize is declared in a shader but isn't used
TEST_P(PointSpritesTest,PointSizeDeclaredButUnused)402 TEST_P(PointSpritesTest, PointSizeDeclaredButUnused)
403 {
404 const std::string vs = SHADER_SOURCE
405 (
406 attribute highp vec4 position;
407
408 void main(void)
409 {
410 gl_PointSize = 1.0;
411 gl_Position = position;
412 }
413 );
414
415 const std::string fs = SHADER_SOURCE
416 (
417 void main(void)
418 {
419 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
420 }
421 );
422
423 GLuint program = CompileProgram(vs, fs);
424 ASSERT_NE(program, 0u);
425 ASSERT_GL_NO_ERROR();
426
427 glUseProgram(program);
428 drawQuad(program, "position", 0.5f, 1.0f);
429 ASSERT_GL_NO_ERROR();
430
431 // expect the center pixel to be red
432 EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 0, 255);
433
434 glDeleteProgram(program);
435 }
436
437 // Test to cover a bug where the D3D11 rasterizer state would not be update when switching between
438 // draw types. This causes the cull face to potentially be incorrect when drawing emulated point
439 // spites.
TEST_P(PointSpritesTest,PointSpriteAlternatingDrawTypes)440 TEST_P(PointSpritesTest, PointSpriteAlternatingDrawTypes)
441 {
442 // clang-format off
443 const std::string pointFS = SHADER_SOURCE
444 (
445 precision mediump float;
446 void main()
447 {
448 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
449 }
450 );
451
452 const std::string pointVS = SHADER_SOURCE
453 (
454 void main()
455 {
456 gl_PointSize = 16.0;
457 gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
458 }
459 );
460
461 const std::string quadFS = SHADER_SOURCE
462 (
463 precision mediump float;
464 void main()
465 {
466 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
467 }
468 );
469
470 const std::string quadVS = SHADER_SOURCE
471 (
472 precision mediump float;
473 attribute vec4 pos;
474 void main()
475 {
476 gl_Position = pos;
477 }
478 );
479 // clang-format on
480
481 GLuint pointProgram = CompileProgram(pointVS, pointFS);
482 ASSERT_NE(pointProgram, 0u);
483 ASSERT_GL_NO_ERROR();
484
485 GLuint quadProgram = CompileProgram(quadVS, quadFS);
486 ASSERT_NE(pointProgram, 0u);
487 ASSERT_GL_NO_ERROR();
488
489 glEnable(GL_CULL_FACE);
490 glCullFace(GL_FRONT);
491
492 const GLfloat quadVertices[] = {
493 -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, -1.0f, -1.0f, 0.5f,
494
495 -1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
496 };
497
498 glUseProgram(quadProgram);
499 GLint positionLocation = glGetAttribLocation(quadProgram, "pos");
500 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices);
501 glEnableVertexAttribArray(positionLocation);
502 glDrawArrays(GL_TRIANGLES, 0, 6);
503
504 glUseProgram(pointProgram);
505 glDrawArrays(GL_POINTS, 0, 1);
506 ASSERT_GL_NO_ERROR();
507
508 // expect the center pixel to be green
509 EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 255, 0, 255);
510
511 glDeleteProgram(pointProgram);
512 glDeleteProgram(quadProgram);
513 }
514
515 // Use this to select which configurations (e.g. which renderer, which GLES
516 // major version) these tests should be run against.
517 //
518 // We test on D3D11 9_3 because the existing D3D11 PointSprite implementation
519 // uses Geometry Shaders which are not supported for 9_3.
520 // D3D9 and D3D11 are also tested to ensure no regressions.
521 ANGLE_INSTANTIATE_TEST(PointSpritesTest,
522 ES2_D3D9(),
523 ES2_D3D11(),
524 ES2_D3D11_FL9_3(),
525 ES2_OPENGL(),
526 ES2_OPENGLES());
527