1 //
2 // Copyright 2020 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 // BootAnimationTest.cpp: Tests that make the same gl calls as Android's boot animations
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11
12 #include "common/debug.h"
13 #include "util/test_utils.h"
14
15 using namespace angle;
16
17 // Makes the same GLES 1 calls as Android's default boot animation
18 // The original animation uses 2 different images -
19 // One image acts as a mask and one that moves(a gradient that acts as a shining light)
20 // We do the same here except with different images of much smaller resolution
21 // The results of each frame of the animation are compared against expected values
22 // The original source of the boot animation can be found here:
23 // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/cmds/bootanimation/BootAnimation.cpp#422
24 class BootAnimationTest : public ANGLETest<>
25 {
26 protected:
BootAnimationTest()27 BootAnimationTest()
28 {
29 setWindowWidth(kWindowWidth);
30 setWindowHeight(kWindowHeight);
31 setConfigRedBits(8);
32 setConfigGreenBits(8);
33 setConfigBlueBits(8);
34 setConfigAlphaBits(8);
35 }
36
initTextureWithData(GLuint * texture,const void * data,GLint width,GLint height,unsigned int channels)37 void initTextureWithData(GLuint *texture,
38 const void *data,
39 GLint width,
40 GLint height,
41 unsigned int channels)
42 {
43 GLint crop[4] = {0, height, width, -height};
44
45 glGenTextures(1, texture);
46 glBindTexture(GL_TEXTURE_2D, *texture);
47
48 switch (channels)
49 {
50 case 3:
51 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
52 GL_UNSIGNED_SHORT_5_6_5, data);
53 break;
54 case 4:
55 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
56 data);
57 break;
58 default:
59 UNREACHABLE();
60 }
61
62 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
63 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
64 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
65 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
66 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
67 }
68
testSetUp()69 void testSetUp() override
70 {
71 EGLWindow *window = getEGLWindow();
72 mDisplay = window->getDisplay();
73 mSurface = window->getSurface();
74
75 /**
76 * The mask is a 4 by 1 texture colored:
77 * --- --- --- ---
78 * |B| |A| |B| |A|
79 * --- --- --- ---
80 * B is black, A is black with alpha of 0xFF
81 */
82 constexpr GLubyte kMask[] = {
83 0x0, 0x0, 0x0, 0xff, // black
84 0x0, 0x0, 0x0, 0x0, // transparent black
85 0x0, 0x0, 0x0, 0xff, // black
86 0x0, 0x0, 0x0, 0x0 // transparent black
87 };
88 /**
89 * The shine is a 8 by 1 texture colored:
90 * --- --- --- --- --- --- --- ---
91 * |R| |R| |G| |G| |B| |B| |W| |W|
92 * --- --- --- --- --- --- --- ---
93 * R is red, G is green, B is blue, W is white
94 */
95 constexpr GLushort kShine[] = {0xF800, // 2 red pixels
96 0xF800,
97 0x07E0, // 2 green pixels
98 0x07E0,
99 0x001F, // 2 blue pixels
100 0x001F,
101 0xFFFF, // 2 white pixels
102 0xFFFF};
103
104 constexpr unsigned int kMaskColorChannels = 4;
105 constexpr unsigned int kShineColorChannels = 3;
106
107 initTextureWithData(&mTextureNames[0], kMask, kMaskWidth, kMaskHeight, kMaskColorChannels);
108 initTextureWithData(&mTextureNames[1], kShine, kShineWidth, kShineHeight,
109 kShineColorChannels);
110
111 // clear screen
112 glShadeModel(GL_FLAT);
113 glDisable(GL_DITHER);
114 glDisable(GL_SCISSOR_TEST);
115 glClearColor(0, 1, 1, 1);
116 glClear(GL_COLOR_BUFFER_BIT);
117 eglSwapBuffers(mDisplay, mSurface);
118 glEnable(GL_TEXTURE_2D);
119 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
120
121 glScissor(kMaskBoundaryLeft, kMaskBoundaryBottom, kMaskWidth, kMaskHeight);
122
123 // Blend state
124 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
125 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
126 }
127
testTearDown()128 void testTearDown() override
129 {
130 glDeleteTextures(1, &mTextureNames[0]);
131 glDeleteTextures(1, &mTextureNames[1]);
132 }
133
checkMaskColor(unsigned int iterationCount,unsigned int maskSlot)134 void checkMaskColor(unsigned int iterationCount, unsigned int maskSlot)
135 {
136 // kOffset is necessary as the visible part is the left most section of the shine
137 // but then we shift the images right
138 constexpr unsigned int kOffset = 7;
139
140 // this solves for the color at any given position in our shine equivalent
141 constexpr unsigned int kPossibleColors = 4;
142 constexpr unsigned int kColorsInARow = 2;
143 unsigned int color =
144 ((iterationCount - maskSlot + kOffset) / kColorsInARow) % kPossibleColors;
145 switch (color)
146 {
147 case 0: // white
148 EXPECT_PIXEL_EQ(kMaskBoundaryLeft + maskSlot, kMaskBoundaryBottom, 0xFF, 0xFF, 0xFF,
149 0xFF);
150 break;
151 case 1: // blue
152 EXPECT_PIXEL_EQ(kMaskBoundaryLeft + maskSlot, kMaskBoundaryBottom, 0x00, 0x00, 0xFF,
153 0xFF);
154 break;
155 case 2: // green
156 EXPECT_PIXEL_EQ(kMaskBoundaryLeft + maskSlot, kMaskBoundaryBottom, 0x00, 0xFF, 0x00,
157 0xFF);
158 break;
159 case 3: // red
160 EXPECT_PIXEL_EQ(kMaskBoundaryLeft + maskSlot, kMaskBoundaryBottom, 0xFF, 0x00, 0x00,
161 0xFF);
162 break;
163 default:
164 UNREACHABLE();
165 }
166 }
167
checkClearColor()168 void checkClearColor()
169 {
170 // Areas outside of the 4x1 mask area should be the clear color due to our glScissor call
171 constexpr unsigned int kImageHeight = 1;
172 EXPECT_PIXEL_RECT_EQ(0, 0, kWindowWidth, kMaskBoundaryBottom, GLColor::cyan);
173 EXPECT_PIXEL_RECT_EQ(0, kMaskBoundaryBottom + kImageHeight, kWindowWidth,
174 (kWindowHeight - (kMaskBoundaryBottom + kImageHeight)), GLColor::cyan);
175 EXPECT_PIXEL_RECT_EQ(0, kMaskBoundaryBottom, kMaskBoundaryLeft, kImageHeight,
176 GLColor::cyan);
177 EXPECT_PIXEL_RECT_EQ(kMaskBoundaryLeft + kMaskWidth, kMaskBoundaryBottom,
178 (kWindowWidth - (kMaskBoundaryLeft + kMaskWidth)), kImageHeight,
179 GLColor::cyan);
180 }
181
validateColors(unsigned int iterationCount)182 void validateColors(unsigned int iterationCount)
183 {
184 // validate all slots in our mask
185 for (unsigned int maskSlot = 0; maskSlot < kMaskWidth; ++maskSlot)
186 {
187 // parts that are blocked in our mask are black
188 switch (maskSlot)
189 {
190 case kBlackMask[0]:
191 case kBlackMask[1]:
192 // slots with non zero alpha are black
193 EXPECT_PIXEL_EQ(kMaskBoundaryLeft + maskSlot, kMaskBoundaryBottom, 0x00, 0x00,
194 0x00, 0xFF);
195 continue;
196 default:
197 checkMaskColor(iterationCount, maskSlot);
198 }
199 }
200 // validate surrounding pixels are equal to clear color
201 checkClearColor();
202 }
203
204 EGLDisplay mDisplay = EGL_NO_DISPLAY;
205 EGLSurface mSurface = EGL_NO_SURFACE;
206 GLuint mTextureNames[2];
207 // This creates a kWindowWidth x kWindowHeight window.
208 // A kMaskWidth by kMaskHeight rectangle is lit up by the shine
209 // This lit up rectangle is positioned at (kMaskBoundaryLeft, kMaskBoundaryBottom)
210 // The border around the area is cleared to GLColor::cyan
211 static constexpr GLint kMaskBoundaryLeft = 15;
212 static constexpr GLint kMaskBoundaryBottom = 7;
213 static constexpr unsigned int kMaskWidth = 4;
214 static constexpr unsigned int kMaskHeight = 1;
215 static constexpr unsigned int kShineWidth = 8;
216 static constexpr unsigned int kShineHeight = 1;
217 static constexpr unsigned int kWindowHeight = 16;
218 static constexpr unsigned int kWindowWidth = 32;
219 static constexpr unsigned int kBlackMask[2] = {0, 2};
220 };
221
TEST_P(BootAnimationTest,DefaultBootAnimation)222 TEST_P(BootAnimationTest, DefaultBootAnimation)
223 {
224 // http://anglebug.com/5085
225 ANGLE_SKIP_TEST_IF(IsWindows() && IsNVIDIA() && IsVulkan());
226
227 constexpr uint64_t kMaxIterationCount = 8; // number of times we shift the shine textures
228 constexpr int kStartingShinePosition = kMaskBoundaryLeft - kShineWidth;
229 constexpr int kEndingShinePosition = kMaskBoundaryLeft;
230 GLint x = kStartingShinePosition;
231 uint64_t iterationCount = 0;
232 do
233 {
234 glDisable(GL_SCISSOR_TEST);
235 glClear(GL_COLOR_BUFFER_BIT);
236 glEnable(GL_SCISSOR_TEST);
237 glDisable(GL_BLEND);
238 glBindTexture(GL_TEXTURE_2D, mTextureNames[1]);
239 glDrawTexiOES(x, kMaskBoundaryBottom, 0, kShineWidth, kShineHeight);
240 glDrawTexiOES(x + kShineWidth, kMaskBoundaryBottom, 0, kShineWidth, kShineHeight);
241 glEnable(GL_BLEND);
242 glBindTexture(GL_TEXTURE_2D, mTextureNames[0]);
243 glDrawTexiOES(kMaskBoundaryLeft, kMaskBoundaryBottom, 0, kMaskWidth, kMaskHeight);
244 validateColors(iterationCount);
245 EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
246 if (res == EGL_FALSE)
247 {
248 break;
249 }
250
251 if (x == kEndingShinePosition)
252 {
253 x = kStartingShinePosition;
254 }
255 ++x;
256 ++iterationCount;
257 } while (iterationCount < kMaxIterationCount);
258 }
259
260 ANGLE_INSTANTIATE_TEST_ES1(BootAnimationTest);