1 //
2 // Copyright 2017 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 // EGLProgramCacheControlTest:
7 // Unit tests for the EGL_ANGLE_program_cache_control extension.
8
9 #include "common/angleutils.h"
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/gl_raii.h"
12 #include "util/EGLWindow.h"
13
14 using namespace angle;
15
16 constexpr EGLint kEnabledCacheSize = 0x10000;
17 constexpr char kEGLExtName[] = "EGL_ANGLE_program_cache_control";
18
19 void TestCacheProgram(PlatformMethods *platform,
20 const ProgramKeyType &key,
21 size_t programSize,
22 const uint8_t *programBytes);
23
24 class EGLProgramCacheControlTest : public ANGLETest
25 {
26 public:
onCache(const ProgramKeyType & key,size_t programSize,const uint8_t * programBytes)27 void onCache(const ProgramKeyType &key, size_t programSize, const uint8_t *programBytes)
28 {
29 mCachedKey = key;
30 mCachedBinary.assign(&programBytes[0], &programBytes[programSize]);
31 }
32
33 protected:
EGLProgramCacheControlTest()34 EGLProgramCacheControlTest()
35 {
36 // Test flakiness was noticed when reusing displays.
37 forceNewDisplay();
38 setDeferContextInit(true);
39 setContextProgramCacheEnabled(true);
40 gDefaultPlatformMethods.cacheProgram = TestCacheProgram;
41 }
42
testSetUp()43 void testSetUp() override
44 {
45 if (extensionAvailable())
46 {
47 EGLDisplay display = getEGLWindow()->getDisplay();
48 eglProgramCacheResizeANGLE(display, kEnabledCacheSize, EGL_PROGRAM_CACHE_RESIZE_ANGLE);
49 ASSERT_EGL_SUCCESS();
50 }
51
52 ASSERT_TRUE(getEGLWindow()->initializeContext());
53 }
54
testTearDown()55 void testTearDown() override { gDefaultPlatformMethods.cacheProgram = DefaultCacheProgram; }
56
extensionAvailable()57 bool extensionAvailable()
58 {
59 EGLDisplay display = getEGLWindow()->getDisplay();
60 return IsEGLDisplayExtensionEnabled(display, kEGLExtName);
61 }
62
programBinaryAvailable()63 bool programBinaryAvailable()
64 {
65 return (getClientMajorVersion() >= 3 || IsGLExtensionEnabled("GL_OES_get_program_binary"));
66 }
67
68 ProgramKeyType mCachedKey;
69 std::vector<uint8_t> mCachedBinary;
70 };
71
TestCacheProgram(PlatformMethods * platform,const ProgramKeyType & key,size_t programSize,const uint8_t * programBytes)72 void TestCacheProgram(PlatformMethods *platform,
73 const ProgramKeyType &key,
74 size_t programSize,
75 const uint8_t *programBytes)
76 {
77 auto *testPlatformContext = static_cast<TestPlatformContext *>(platform->context);
78 auto *testCase =
79 reinterpret_cast<EGLProgramCacheControlTest *>(testPlatformContext->currentTest);
80 testCase->onCache(key, programSize, programBytes);
81 }
82
83 // Tests error conditions of the APIs.
TEST_P(EGLProgramCacheControlTest,NegativeAPI)84 TEST_P(EGLProgramCacheControlTest, NegativeAPI)
85 {
86 ANGLE_SKIP_TEST_IF(!extensionAvailable());
87
88 constexpr char kDefaultKey[] = "defaultMakeItLongEnough";
89 constexpr char kDefaultBinary[] = "defaultMakeItLongEnough";
90 constexpr EGLint kDefaultKeySize = static_cast<EGLint>(ArraySize(kDefaultKey));
91 constexpr EGLint kDefaultBinarySize = static_cast<EGLint>(ArraySize(kDefaultBinary));
92
93 // Test that passing an invalid display to the entry point methods fails.
94 eglProgramCacheGetAttribANGLE(EGL_NO_DISPLAY, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
95 EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
96
97 eglProgramCachePopulateANGLE(EGL_NO_DISPLAY, kDefaultKey, kDefaultKeySize, kDefaultBinary,
98 kDefaultBinarySize);
99 EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
100
101 EGLint tempKeySize = 0;
102 EGLint tempBinarySize = 0;
103 eglProgramCacheQueryANGLE(EGL_NO_DISPLAY, 0, nullptr, &tempKeySize, nullptr, &tempBinarySize);
104 EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
105
106 eglProgramCacheResizeANGLE(EGL_NO_DISPLAY, 0, EGL_PROGRAM_CACHE_TRIM_ANGLE);
107 EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
108
109 // Test querying properties with bad parameters.
110 EGLDisplay display = getEGLWindow()->getDisplay();
111 eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_RESIZE_ANGLE);
112 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
113
114 // Test populating with invalid parameters.
115 EGLint keySize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
116 EXPECT_GT(kDefaultKeySize, keySize);
117 eglProgramCachePopulateANGLE(display, kDefaultKey, keySize + 1, kDefaultBinary,
118 kDefaultBinarySize);
119 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
120
121 eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, kDefaultBinary, -1);
122 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
123
124 eglProgramCachePopulateANGLE(display, nullptr, keySize, kDefaultBinary, kDefaultBinarySize);
125 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
126
127 eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, nullptr, kDefaultBinarySize);
128 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
129
130 // Test querying cache entries with invalid parameters.
131 eglProgramCachePopulateANGLE(display, kDefaultKey, keySize, kDefaultBinary, kDefaultBinarySize);
132 ASSERT_EGL_SUCCESS();
133
134 EGLint cacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
135 ASSERT_EQ(1, cacheSize);
136
137 eglProgramCacheQueryANGLE(display, -1, nullptr, &tempKeySize, nullptr, &tempBinarySize);
138 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
139
140 eglProgramCacheQueryANGLE(display, 1, nullptr, &tempKeySize, nullptr, &tempBinarySize);
141 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
142
143 eglProgramCacheQueryANGLE(display, 0, nullptr, nullptr, nullptr, &tempBinarySize);
144 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
145
146 eglProgramCacheQueryANGLE(display, 0, nullptr, &tempKeySize, nullptr, nullptr);
147 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
148
149 eglProgramCacheQueryANGLE(display, 0, nullptr, &tempKeySize, nullptr, &tempBinarySize);
150 ASSERT_EGL_SUCCESS();
151 ASSERT_EQ(keySize, tempKeySize);
152 ASSERT_EQ(kDefaultBinarySize, tempBinarySize);
153
154 std::vector<uint8_t> tempKey(tempKeySize + 5);
155 std::vector<uint8_t> tempBinary(tempBinarySize + 5);
156
157 tempKeySize--;
158
159 eglProgramCacheQueryANGLE(display, 0, tempKey.data(), &tempKeySize, tempBinary.data(),
160 &tempBinarySize);
161 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
162
163 tempKeySize++;
164 tempBinarySize--;
165
166 eglProgramCacheQueryANGLE(display, 0, tempKey.data(), &tempKeySize, tempBinary.data(),
167 &tempBinarySize);
168 EXPECT_EGL_ERROR(EGL_BAD_ACCESS);
169
170 // Test resizing with invalid parameters.
171 eglProgramCacheResizeANGLE(display, -1, EGL_PROGRAM_CACHE_TRIM_ANGLE);
172 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
173
174 eglProgramCacheResizeANGLE(display, 0, EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE);
175 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
176 }
177
178 // Tests a basic use case.
TEST_P(EGLProgramCacheControlTest,SaveAndReload)179 TEST_P(EGLProgramCacheControlTest, SaveAndReload)
180 {
181 ANGLE_SKIP_TEST_IF(!extensionAvailable() || !programBinaryAvailable());
182
183 constexpr char kVS[] = "attribute vec4 position; void main() { gl_Position = position; }";
184 constexpr char kFS[] = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }";
185
186 // Link a program, which will miss the cache.
187 {
188 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
189 glClear(GL_COLOR_BUFFER_BIT);
190 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
191
192 ANGLE_GL_PROGRAM(program, kVS, kFS);
193 drawQuad(program, "position", 0.5f);
194 EXPECT_GL_NO_ERROR();
195 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
196 }
197
198 EGLDisplay display = getEGLWindow()->getDisplay();
199 EGLint cacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
200 EXPECT_EQ(1, cacheSize);
201
202 EGLint keySize = 0;
203 EGLint binarySize = 0;
204 eglProgramCacheQueryANGLE(display, 0, nullptr, &keySize, nullptr, &binarySize);
205 EXPECT_EQ(static_cast<EGLint>(mCachedKey.size()), keySize);
206 ASSERT_EGL_SUCCESS();
207
208 ProgramKeyType keyBuffer;
209 std::vector<uint8_t> binaryBuffer(binarySize);
210 eglProgramCacheQueryANGLE(display, 0, keyBuffer.data(), &keySize, binaryBuffer.data(),
211 &binarySize);
212 ASSERT_EGL_SUCCESS();
213
214 EXPECT_EQ(mCachedKey, keyBuffer);
215 EXPECT_EQ(mCachedBinary, binaryBuffer);
216
217 // Restart EGL and GL.
218 recreateTestFixture();
219
220 // Warm up the cache.
221 EGLint newCacheSize = eglProgramCacheGetAttribANGLE(display, EGL_PROGRAM_CACHE_SIZE_ANGLE);
222 EXPECT_EQ(0, newCacheSize);
223 eglProgramCachePopulateANGLE(display, keyBuffer.data(), keySize, binaryBuffer.data(),
224 binarySize);
225
226 mCachedBinary.clear();
227
228 // Link a program, which will hit the cache.
229 {
230 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
231 glClear(GL_COLOR_BUFFER_BIT);
232 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
233
234 ANGLE_GL_PROGRAM(program, kVS, kFS);
235 drawQuad(program, "position", 0.5f);
236 EXPECT_GL_NO_ERROR();
237 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
238 }
239
240 // Verify no new shader was compiled.
241 EXPECT_TRUE(mCachedBinary.empty());
242 }
243
244 // Tests that trying to link a program without correct shaders doesn't buggily call the cache.
TEST_P(EGLProgramCacheControlTest,LinkProgramWithBadShaders)245 TEST_P(EGLProgramCacheControlTest, LinkProgramWithBadShaders)
246 {
247 ANGLE_SKIP_TEST_IF(!extensionAvailable());
248
249 GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
250
251 GLuint program = glCreateProgram();
252 glAttachShader(program, shader);
253 glLinkProgram(program);
254
255 GLint linkStatus = 0;
256 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
257 EXPECT_GL_FALSE(linkStatus);
258 EXPECT_GL_NO_ERROR();
259
260 glDeleteShader(shader);
261 glDeleteProgram(program);
262 }
263
264 ANGLE_INSTANTIATE_TEST(EGLProgramCacheControlTest,
265 ES2_D3D9(),
266 ES2_D3D11(),
267 ES2_OPENGL(),
268 ES2_VULKAN());
269