• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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