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
7 #include "test_utils/ANGLETest.h"
8 #include "test_utils/gl_raii.h"
9
10 using namespace angle;
11
12 namespace
13 {
14
15 class ViewportTest : public ANGLETest
16 {
17 protected:
ViewportTest()18 ViewportTest()
19 {
20 setWindowWidth(512);
21 setWindowHeight(512);
22 setConfigRedBits(8);
23 setConfigGreenBits(8);
24 setConfigBlueBits(8);
25 setConfigAlphaBits(8);
26 setConfigDepthBits(24);
27
28 mProgram = 0;
29 }
30
runNonScissoredTest()31 void runNonScissoredTest()
32 {
33 glClearColor(0, 0, 0, 1);
34 glClear(GL_COLOR_BUFFER_BIT);
35
36 runTest();
37 }
38
runScissoredTest()39 void runScissoredTest()
40 {
41 glClearColor(0, 0, 0, 1);
42 glClear(GL_COLOR_BUFFER_BIT);
43
44 glEnable(GL_SCISSOR_TEST);
45 glScissor(0, getWindowHeight() / 2, getWindowWidth(), getWindowHeight() / 2);
46
47 runTest();
48 }
49
runTest()50 void runTest()
51 {
52 // Firstly ensure that no errors have been hit.
53 EXPECT_GL_NO_ERROR();
54
55 GLint viewportSize[4];
56 glGetIntegerv(GL_VIEWPORT, viewportSize);
57
58 // Clear to green. Might be a scissored clear, if scissorSize != window size
59 glClearColor(0, 1, 0, 1);
60 glClear(GL_COLOR_BUFFER_BIT);
61
62 // Draw a red quad centered in the middle of the viewport, with dimensions 25% of the size
63 // of the viewport.
64 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f, 0.25f);
65
66 GLint centerViewportX = viewportSize[0] + (viewportSize[2] / 2);
67 GLint centerViewportY = viewportSize[1] + (viewportSize[3] / 2);
68
69 GLint redQuadLeftSideX = viewportSize[0] + viewportSize[2] * 3 / 8;
70 GLint redQuadRightSideX = viewportSize[0] + viewportSize[2] * 5 / 8;
71 GLint redQuadTopSideY = viewportSize[1] + viewportSize[3] * 3 / 8;
72 GLint redQuadBottomSideY = viewportSize[1] + viewportSize[3] * 5 / 8;
73
74 // The midpoint of the viewport should be red.
75 checkPixel(centerViewportX, centerViewportY, true);
76
77 // Pixels just inside the red quad should be red.
78 checkPixel(redQuadLeftSideX, redQuadTopSideY, true);
79 checkPixel(redQuadLeftSideX, redQuadBottomSideY - 1, true);
80 checkPixel(redQuadRightSideX - 1, redQuadTopSideY, true);
81 checkPixel(redQuadRightSideX - 1, redQuadBottomSideY - 1, true);
82
83 // Pixels just outside the red quad shouldn't be red.
84 checkPixel(redQuadLeftSideX - 1, redQuadTopSideY - 1, false);
85 checkPixel(redQuadLeftSideX - 1, redQuadBottomSideY, false);
86 checkPixel(redQuadRightSideX, redQuadTopSideY - 1, false);
87 checkPixel(redQuadRightSideX, redQuadBottomSideY, false);
88
89 // Pixels just within the viewport shouldn't be red.
90 checkPixel(viewportSize[0], viewportSize[1], false);
91 checkPixel(viewportSize[0], viewportSize[1] + viewportSize[3] - 1, false);
92 checkPixel(viewportSize[0] + viewportSize[2] - 1, viewportSize[1], false);
93 checkPixel(viewportSize[0] + viewportSize[2] - 1, viewportSize[1] + viewportSize[3] - 1,
94 false);
95 }
96
checkPixel(GLint x,GLint y,GLboolean renderedRed)97 void checkPixel(GLint x, GLint y, GLboolean renderedRed)
98 {
99 // By default, expect the pixel to be black.
100 GLint expectedRedChannel = 0;
101 GLint expectedGreenChannel = 0;
102
103 GLint scissorSize[4];
104 glGetIntegerv(GL_SCISSOR_BOX, scissorSize);
105
106 EXPECT_GL_NO_ERROR();
107
108 if (scissorSize[0] <= x && x < scissorSize[0] + scissorSize[2] && scissorSize[1] <= y &&
109 y < scissorSize[1] + scissorSize[3])
110 {
111 // If the pixel lies within the scissor rect, then it should have been cleared to green.
112 // If we rendered a red square on top of it, then the pixel should be red (the green
113 // channel will have been reset to 0).
114 expectedRedChannel = renderedRed ? 255 : 0;
115 expectedGreenChannel = renderedRed ? 0 : 255;
116 }
117
118 // If the pixel is within the bounds of the window, then we check it. Otherwise we skip it.
119 if (0 <= x && x < getWindowWidth() && 0 <= y && y < getWindowHeight())
120 {
121 EXPECT_PIXEL_EQ(x, y, expectedRedChannel, expectedGreenChannel, 0, 255);
122 }
123 }
124
testSetUp()125 void testSetUp() override
126 {
127 mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
128 if (mProgram == 0)
129 {
130 FAIL() << "shader compilation failed.";
131 }
132
133 glUseProgram(mProgram);
134
135 glClearColor(0, 0, 0, 1);
136 glClearDepthf(0.0);
137 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
138
139 // Call glViewport and glScissor with default parameters.
140 glScissor(0, 0, getWindowWidth(), getWindowHeight());
141 glViewport(0, 0, getWindowWidth(), getWindowHeight());
142
143 glDisable(GL_DEPTH_TEST);
144 }
145
testTearDown()146 void testTearDown() override { glDeleteProgram(mProgram); }
147
148 GLuint mProgram;
149 };
150
TEST_P(ViewportTest,QuarterWindow)151 TEST_P(ViewportTest, QuarterWindow)
152 {
153 glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
154
155 runNonScissoredTest();
156
157 runScissoredTest();
158 }
159
TEST_P(ViewportTest,QuarterWindowCentered)160 TEST_P(ViewportTest, QuarterWindowCentered)
161 {
162 glViewport(getWindowWidth() * 3 / 8, getWindowHeight() * 3 / 8, getWindowWidth() / 4,
163 getWindowHeight() / 4);
164
165 runNonScissoredTest();
166
167 runScissoredTest();
168 }
169
TEST_P(ViewportTest,FullWindow)170 TEST_P(ViewportTest, FullWindow)
171 {
172 glViewport(0, 0, getWindowWidth(), getWindowHeight());
173
174 runNonScissoredTest();
175
176 runScissoredTest();
177 }
178
TEST_P(ViewportTest,FullWindowOffCenter)179 TEST_P(ViewportTest, FullWindowOffCenter)
180 {
181 glViewport(-getWindowWidth() / 2, getWindowHeight() / 2, getWindowWidth(), getWindowHeight());
182
183 runNonScissoredTest();
184
185 runScissoredTest();
186 }
187
TEST_P(ViewportTest,DoubleWindow)188 TEST_P(ViewportTest, DoubleWindow)
189 {
190 glViewport(0, 0, getWindowWidth() * 2, getWindowHeight() * 2);
191
192 runNonScissoredTest();
193
194 runScissoredTest();
195 }
196
TEST_P(ViewportTest,DoubleWindowCentered)197 TEST_P(ViewportTest, DoubleWindowCentered)
198 {
199 glViewport(-getWindowWidth() / 2, -getWindowHeight() / 2, getWindowWidth() * 2,
200 getWindowHeight() * 2);
201
202 runNonScissoredTest();
203
204 runScissoredTest();
205 }
206
TEST_P(ViewportTest,DoubleWindowOffCenter)207 TEST_P(ViewportTest, DoubleWindowOffCenter)
208 {
209 glViewport(-getWindowWidth() * 3 / 4, getWindowHeight() * 3 / 4, getWindowWidth(),
210 getWindowHeight());
211
212 runNonScissoredTest();
213
214 runScissoredTest();
215 }
216
TEST_P(ViewportTest,TripleWindow)217 TEST_P(ViewportTest, TripleWindow)
218 {
219 glViewport(0, 0, getWindowWidth() * 3, getWindowHeight() * 3);
220
221 runNonScissoredTest();
222
223 runScissoredTest();
224 }
225
TEST_P(ViewportTest,TripleWindowCentered)226 TEST_P(ViewportTest, TripleWindowCentered)
227 {
228 glViewport(-getWindowWidth(), -getWindowHeight(), getWindowWidth() * 3, getWindowHeight() * 3);
229
230 runNonScissoredTest();
231
232 runScissoredTest();
233 }
234
TEST_P(ViewportTest,TripleWindowOffCenter)235 TEST_P(ViewportTest, TripleWindowOffCenter)
236 {
237 glViewport(-getWindowWidth() * 3 / 2, -getWindowHeight() * 3 / 2, getWindowWidth() * 3,
238 getWindowHeight() * 3);
239
240 runNonScissoredTest();
241
242 runScissoredTest();
243 }
244
245 // Test line rendering with a non-standard viewport.
TEST_P(ViewportTest,DrawLineWithViewport)246 TEST_P(ViewportTest, DrawLineWithViewport)
247 {
248 // We assume in the test the width and height are equal and we are tracing
249 // the line from bottom left to top right. Verify that all pixels along that line
250 // have been traced with green.
251 ASSERT_EQ(getWindowWidth(), getWindowHeight());
252
253 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
254 glUseProgram(program);
255
256 std::vector<Vector3> vertices = {{-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}};
257
258 const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
259 ASSERT_NE(-1, positionLocation);
260
261 GLBuffer vertexBuffer;
262 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
263 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
264 GL_STATIC_DRAW);
265 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
266 glEnableVertexAttribArray(positionLocation);
267
268 // Set the viewport.
269 GLint quarterWidth = getWindowWidth() / 4;
270 GLint quarterHeight = getWindowHeight() / 4;
271 glViewport(quarterWidth, quarterHeight, quarterWidth, quarterHeight);
272
273 glClear(GL_COLOR_BUFFER_BIT);
274 glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertices.size()));
275
276 glDisableVertexAttribArray(positionLocation);
277
278 ASSERT_GL_NO_ERROR();
279
280 for (GLint x = quarterWidth; x < getWindowWidth() / 2; x++)
281 {
282 EXPECT_PIXEL_COLOR_EQ(x, x, GLColor::green);
283 }
284 }
285
286 // Test line rendering with an overly large viewport.
TEST_P(ViewportTest,DrawLineWithLargeViewport)287 TEST_P(ViewportTest, DrawLineWithLargeViewport)
288 {
289 // We assume in the test the width and height are equal and we are tracing
290 // the line from bottom left to top right. Verify that all pixels along that line
291 // have been traced with green.
292 ASSERT_EQ(getWindowWidth(), getWindowHeight());
293
294 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
295 glUseProgram(program);
296
297 std::vector<Vector3> vertices = {{-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}};
298
299 const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
300 ASSERT_NE(-1, positionLocation);
301
302 GLBuffer vertexBuffer;
303 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
304 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
305 GL_STATIC_DRAW);
306 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
307 glEnableVertexAttribArray(positionLocation);
308
309 // Set the viewport.
310 glViewport(0, 0, getWindowWidth() * 2, getWindowHeight() * 2);
311
312 glClear(GL_COLOR_BUFFER_BIT);
313 glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertices.size()));
314
315 glDisableVertexAttribArray(positionLocation);
316
317 ASSERT_GL_NO_ERROR();
318
319 for (GLint x = 0; x < getWindowWidth(); x++)
320 {
321 EXPECT_PIXEL_COLOR_EQ(x, x, GLColor::green);
322 }
323 }
324
325 // Test very large viewport sizes so sanitizers can verify there is no undefined behaviour
TEST_P(ViewportTest,Overflow)326 TEST_P(ViewportTest, Overflow)
327 {
328 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
329 glUseProgram(program);
330
331 std::vector<Vector3> vertices = {{-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}};
332
333 const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
334 ASSERT_NE(-1, positionLocation);
335
336 GLBuffer vertexBuffer;
337 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
338 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
339 GL_STATIC_DRAW);
340 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
341 glEnableVertexAttribArray(positionLocation);
342
343 constexpr int kMaxSize = std::numeric_limits<int>::max();
344 const int kTestViewportSizes[][4] = {
345 {
346 kMaxSize,
347 kMaxSize,
348 1,
349 1,
350 },
351 {
352 0,
353 0,
354 kMaxSize,
355 kMaxSize,
356 },
357 {
358 1,
359 1,
360 kMaxSize,
361 kMaxSize,
362 },
363 {
364 kMaxSize,
365 kMaxSize,
366 kMaxSize,
367 kMaxSize,
368 },
369 };
370
371 for (const int *viewportSize : kTestViewportSizes)
372 {
373 // Set the viewport.
374 glViewport(viewportSize[0], viewportSize[1], viewportSize[2], viewportSize[3]);
375
376 glClear(GL_COLOR_BUFFER_BIT);
377 glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertices.size()));
378
379 glDisableVertexAttribArray(positionLocation);
380
381 ASSERT_GL_NO_ERROR();
382 }
383 }
384
385 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
386 // tests should be run against. D3D11 Feature Level 9 and D3D9 emulate large and negative viewports
387 // in the vertex shader. We should test both of these as well as D3D11 Feature Level 10_0+.
388 ANGLE_INSTANTIATE_TEST(ViewportTest,
389 ES2_D3D9(),
390 ES2_D3D11(),
391 ES2_D3D11_PRESENT_PATH_FAST(),
392 ES2_OPENGLES(),
393 ES3_OPENGLES(),
394 ES2_VULKAN());
395
396 // This test suite is not instantiated on some OSes.
397 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ViewportTest);
398
399 } // namespace
400