1 //
2 // Copyright 2019 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 // Tests the eglQueryStringiANGLE and eglQueryDisplayAttribANGLE functions exposed by the
7 // extension EGL_ANGLE_feature_control.
8
9 #include <gtest/gtest.h>
10 #include <optional>
11
12 #include "common/string_utils.h"
13 #include "libANGLE/Display.h"
14 #include "test_utils/ANGLETest.h"
15
16 using namespace angle;
17
18 class EGLFeatureControlTest : public ANGLETest<>
19 {
20 public:
testSetUp()21 void testSetUp() override { mDisplay = EGL_NO_DISPLAY; }
22
testTearDown()23 void testTearDown() override
24 {
25 if (mDisplay != EGL_NO_DISPLAY)
26 {
27 eglTerminate(mDisplay);
28 }
29 }
30
31 protected:
32 EGLDisplay mDisplay;
33
initTest()34 bool initTest()
35 {
36 // http://anglebug.com/42262291 This test sporadically times out on Win10/Intel
37 if (IsWindows() && IsIntel())
38 return false;
39
40 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
41 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
42 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
43 EXPECT_NE(mDisplay, EGL_NO_DISPLAY);
44
45 EXPECT_EQ(eglInitialize(mDisplay, nullptr, nullptr), static_cast<EGLBoolean>(EGL_TRUE));
46
47 EXPECT_TRUE(IsEGLClientExtensionEnabled("EGL_ANGLE_feature_control"));
48
49 return true;
50 }
51
52 using FeatureNameModifier = std::function<std::string(const std::string &)>;
53 void testOverrideFeatures(FeatureNameModifier modifyName);
54 };
55
56 // Ensure eglQueryStringiANGLE generates EGL_BAD_DISPLAY if the display passed in is invalid.
TEST_P(EGLFeatureControlTest,InvalidDisplay)57 TEST_P(EGLFeatureControlTest, InvalidDisplay)
58 {
59 ANGLE_SKIP_TEST_IF(!initTest());
60 EXPECT_EQ(nullptr, eglQueryStringiANGLE(EGL_NO_DISPLAY, EGL_FEATURE_NAME_ANGLE, 0));
61 EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
62 }
63
64 // Ensure eglQueryStringiANGLE generates EGL_BAD_PARAMETER if the index is negative.
TEST_P(EGLFeatureControlTest,NegativeIndex)65 TEST_P(EGLFeatureControlTest, NegativeIndex)
66 {
67 ANGLE_SKIP_TEST_IF(!initTest());
68 EXPECT_EQ(nullptr, eglQueryStringiANGLE(mDisplay, EGL_FEATURE_NAME_ANGLE, -1));
69 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
70 }
71
72 // Ensure eglQueryStringiANGLE generates EGL_BAD_PARAMETER if the index is out of bounds.
TEST_P(EGLFeatureControlTest,IndexOutOfBounds)73 TEST_P(EGLFeatureControlTest, IndexOutOfBounds)
74 {
75 ANGLE_SKIP_TEST_IF(!initTest());
76 egl::Display *display = static_cast<egl::Display *>(mDisplay);
77 EXPECT_EQ(nullptr, eglQueryStringiANGLE(mDisplay, EGL_FEATURE_NAME_ANGLE,
78 display->getFeatures().size()));
79 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
80 }
81
82 // Ensure eglQueryStringiANGLE generates EGL_BAD_PARAMETER if the name is not one of the valid
83 // options specified in EGL_ANGLE_feature_control.
TEST_P(EGLFeatureControlTest,InvalidName)84 TEST_P(EGLFeatureControlTest, InvalidName)
85 {
86 ANGLE_SKIP_TEST_IF(!initTest());
87 EXPECT_EQ(nullptr, eglQueryStringiANGLE(mDisplay, 100, 0));
88 EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
89 }
90
91 // For each valid name and index in the feature description arrays, query the values and ensure
92 // that no error is generated, and that the values match the correct values frim ANGLE's display's
93 // FeatureList.
TEST_P(EGLFeatureControlTest,QueryAll)94 TEST_P(EGLFeatureControlTest, QueryAll)
95 {
96 ANGLE_SKIP_TEST_IF(!initTest());
97 egl::Display *display = static_cast<egl::Display *>(mDisplay);
98 angle::FeatureList features = display->getFeatures();
99 for (size_t i = 0; i < features.size(); i++)
100 {
101 EXPECT_STREQ(features[i]->name, eglQueryStringiANGLE(mDisplay, EGL_FEATURE_NAME_ANGLE, i));
102 EXPECT_STREQ(FeatureCategoryToString(features[i]->category),
103 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_CATEGORY_ANGLE, i));
104 EXPECT_STREQ(FeatureStatusToString(features[i]->enabled),
105 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i));
106 ASSERT_EGL_SUCCESS();
107 }
108 }
109
110 // Ensure eglQueryDisplayAttribANGLE returns the correct number of features when queried with
111 // attribute EGL_FEATURE_COUNT_ANGLE
TEST_P(EGLFeatureControlTest,FeatureCount)112 TEST_P(EGLFeatureControlTest, FeatureCount)
113 {
114 ANGLE_SKIP_TEST_IF(!initTest());
115 egl::Display *display = static_cast<egl::Display *>(mDisplay);
116 EGLAttrib value = -1;
117 EXPECT_EQ(static_cast<EGLBoolean>(EGL_TRUE),
118 eglQueryDisplayAttribANGLE(mDisplay, EGL_FEATURE_COUNT_ANGLE, &value));
119 EXPECT_EQ(display->getFeatures().size(), static_cast<size_t>(value));
120 ASSERT_EGL_SUCCESS();
121 }
122
testOverrideFeatures(FeatureNameModifier modifyName)123 void EGLFeatureControlTest::testOverrideFeatures(FeatureNameModifier modifyName)
124 {
125 ANGLE_SKIP_TEST_IF(!initTest());
126 egl::Display *display = static_cast<egl::Display *>(mDisplay);
127 angle::FeatureList features = display->getFeatures();
128
129 // Build lists of features to enable/disabled. Toggle features we know are ok to toggle based
130 // from this list.
131 std::vector<const char *> enabled;
132 std::vector<const char *> disabled;
133 std::vector<std::string> modifiedNameStorage;
134 std::vector<bool> shouldBe;
135 std::vector<std::string> testedFeatures = {
136 // Safe to toggle on GL
137 angle::GetFeatureName(angle::Feature::AddAndTrueToLoopCondition),
138 angle::GetFeatureName(angle::Feature::ClampFragDepth),
139 // Safe to toggle on GL and Vulkan
140 angle::GetFeatureName(angle::Feature::ClampPointSize),
141 // Safe to toggle on D3D
142 angle::GetFeatureName(angle::Feature::ZeroMaxLodWorkaround),
143 angle::GetFeatureName(angle::Feature::ExpandIntegerPowExpressions),
144 angle::GetFeatureName(angle::Feature::RewriteUnaryMinusOperator),
145 };
146
147 modifiedNameStorage.reserve(features.size());
148 shouldBe.reserve(features.size());
149
150 for (size_t i = 0; i < features.size(); i++)
151 {
152 modifiedNameStorage.push_back(modifyName(features[i]->name));
153
154 bool toggle = std::find(testedFeatures.begin(), testedFeatures.end(),
155 std::string(features[i]->name)) != testedFeatures.end();
156 if (features[i]->enabled ^ toggle)
157 {
158 enabled.push_back(modifiedNameStorage[i].c_str());
159 }
160 else
161 {
162 disabled.push_back(modifiedNameStorage[i].c_str());
163 }
164 // Save what we expect the feature status will be when checking later.
165 shouldBe.push_back(features[i]->enabled ^ toggle);
166 }
167 disabled.push_back(0);
168 enabled.push_back(0);
169
170 // Terminate the old display (we just used it to collect features)
171 eglTerminate(mDisplay);
172
173 // Create a new display with these overridden features.
174 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
175 GetParam().getRenderer(),
176 EGL_FEATURE_OVERRIDES_ENABLED_ANGLE,
177 reinterpret_cast<EGLAttrib>(enabled.data()),
178 EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
179 reinterpret_cast<EGLAttrib>(disabled.data()),
180 EGL_NONE};
181 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
182 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
183 ASSERT_EGL_SUCCESS();
184 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
185 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
186
187 // Check that all features have the correct status (even the ones we toggled).
188 for (size_t i = 0; i < features.size(); i++)
189 {
190 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
191 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
192 << modifiedNameStorage[i];
193 }
194 }
195
196 // Submit a list of features to override when creating the display with eglGetPlatformDisplay, and
197 // ensure that the features are correctly overridden.
TEST_P(EGLFeatureControlTest,OverrideFeatures)198 TEST_P(EGLFeatureControlTest, OverrideFeatures)
199 {
200 testOverrideFeatures([](const std::string &featureName) { return featureName; });
201 }
202
203 // Similar to OverrideFeatures, but ensures that camelCase variants of the name match as well.
TEST_P(EGLFeatureControlTest,OverrideFeaturesCamelCase)204 TEST_P(EGLFeatureControlTest, OverrideFeaturesCamelCase)
205 {
206 testOverrideFeatures(
207 [](const std::string &featureName) { return angle::ToCamelCase(featureName); });
208 }
209
210 // Similar to OverrideFeatures, but ensures wildcard matching works
TEST_P(EGLFeatureControlTest,OverrideFeaturesWildcard)211 TEST_P(EGLFeatureControlTest, OverrideFeaturesWildcard)
212 {
213 for (int j = 0; j < 2; j++)
214 {
215 const bool testEnableOverride = (j != 0);
216
217 ANGLE_SKIP_TEST_IF(!initTest());
218
219 egl::Display *display = static_cast<egl::Display *>(mDisplay);
220 angle::FeatureList features = display->getFeatures();
221
222 // Note that we don't use the broader 'prefer_*' here because
223 // prefer_monolithic_pipelines_over_libraries may affect other feature
224 // flags.
225 std::vector<const char *> featuresToOverride = {"prefer_d*", nullptr};
226
227 std::vector<std::string> featureNameStorage;
228 std::vector<bool> shouldBe;
229
230 shouldBe.reserve(features.size());
231 featureNameStorage.reserve(features.size());
232
233 for (size_t i = 0; i < features.size(); i++)
234 {
235 std::string featureName = std::string(features[i]->name);
236 std::transform(featureName.begin(), featureName.end(), featureName.begin(),
237 [](unsigned char c) { return std::tolower(c); });
238
239 const bool featureMatch = strncmp(featureName.c_str(), "preferd", 7) == 0;
240
241 std::optional<bool> overrideState;
242 if (featureMatch)
243 {
244 overrideState = testEnableOverride;
245 }
246
247 // Save what we expect the feature status will be when checking later.
248 shouldBe.push_back(overrideState.value_or(features[i]->enabled));
249 featureNameStorage.push_back(features[i]->name);
250 }
251
252 // Terminate the old display (we just used it to collect features)
253 eglTerminate(mDisplay);
254 mDisplay = nullptr;
255
256 // Create a new display with these overridden features.
257 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
258 testEnableOverride ? EGL_FEATURE_OVERRIDES_ENABLED_ANGLE
259 : EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
260 reinterpret_cast<EGLAttrib>(featuresToOverride.data()), EGL_NONE};
261 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
262 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
263 ASSERT_EGL_SUCCESS();
264 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
265 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
266
267 // Check that all features have the correct status (even the ones we toggled).
268 for (size_t i = 0; i < features.size(); i++)
269 {
270 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
271 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
272 << featureNameStorage[i];
273 }
274
275 // Clean up display for next iteration.
276 eglTerminate(mDisplay);
277 mDisplay = nullptr;
278 }
279 }
280
281 // Ensure that dependent features are affected properly by overrides
TEST_P(EGLFeatureControlTest,OverrideFeaturesDependent)282 TEST_P(EGLFeatureControlTest, OverrideFeaturesDependent)
283 {
284 ANGLE_SKIP_TEST_IF(!initTest());
285
286 egl::Display *display = static_cast<egl::Display *>(mDisplay);
287 angle::FeatureList features = display->getFeatures();
288
289 const std::vector<const char *> featuresDisabled = {
290 GetFeatureName(Feature::SupportsRenderpass2),
291 GetFeatureName(Feature::SupportsImage2dViewOf3d), nullptr};
292
293 const std::vector<const char *> featuresExpectDisabled = {
294 // Features we changed
295 GetFeatureName(Feature::SupportsRenderpass2),
296 GetFeatureName(Feature::SupportsImage2dViewOf3d),
297
298 // Features that must become disabled as a result of the above
299 GetFeatureName(Feature::SupportsDepthStencilResolve),
300 GetFeatureName(Feature::SupportsDepthStencilIndependentResolveNone),
301 GetFeatureName(Feature::SupportsSampler2dViewOf3d),
302 GetFeatureName(Feature::SupportsFragmentShadingRate),
303 };
304
305 std::vector<std::string> featureNameStorage;
306 std::vector<bool> shouldBe;
307
308 shouldBe.reserve(features.size());
309 featureNameStorage.reserve(features.size());
310
311 for (size_t i = 0; i < features.size(); i++)
312 {
313 bool featureMatch = false;
314 for (auto *ptr : featuresExpectDisabled)
315 {
316 if (strcmp(ptr, features[i]->name) == 0)
317 {
318 featureMatch = true;
319 break;
320 }
321 }
322
323 std::string featureName = std::string(features[i]->name);
324 std::transform(featureName.begin(), featureName.end(), featureName.begin(),
325 [](unsigned char c) { return std::tolower(c); });
326
327 // Save what we expect the feature status will be when checking later.
328 shouldBe.push_back(features[i]->enabled && !featureMatch);
329
330 // Store copy of the feature name string, in case we need to print for a test failure
331 featureNameStorage.push_back(features[i]->name);
332 }
333
334 // Terminate the old display (we just used it to collect features)
335 eglTerminate(mDisplay);
336
337 // Create a new display with these overridden features.
338 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
339 EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
340 reinterpret_cast<EGLAttrib>(featuresDisabled.data()), EGL_NONE};
341 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
342 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
343 ASSERT_EGL_SUCCESS();
344 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
345 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
346
347 // Check that all features have the correct status (even the ones we toggled).
348 for (size_t i = 0; i < features.size(); i++)
349 {
350 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
351 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
352 << featureNameStorage[i];
353 }
354 }
355
356 ANGLE_INSTANTIATE_TEST(EGLFeatureControlTest,
357 WithNoFixture(ES2_D3D9()),
358 WithNoFixture(ES2_D3D11()),
359 WithNoFixture(ES2_METAL()),
360 WithNoFixture(ES2_OPENGL()),
361 WithNoFixture(ES2_VULKAN()),
362 WithNoFixture(ES3_D3D11()),
363 WithNoFixture(ES3_METAL()),
364 WithNoFixture(ES3_OPENGL()));
365