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/3629 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(features[i]->description,
105 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_DESCRIPTION_ANGLE, i));
106 EXPECT_STREQ(features[i]->bug, eglQueryStringiANGLE(mDisplay, EGL_FEATURE_BUG_ANGLE, i));
107 EXPECT_STREQ(FeatureStatusToString(features[i]->enabled),
108 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i));
109 EXPECT_STREQ(features[i]->condition,
110 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_CONDITION_ANGLE, i));
111 ASSERT_EGL_SUCCESS();
112 }
113 }
114
115 // Ensure eglQueryDisplayAttribANGLE returns the correct number of features when queried with
116 // attribute EGL_FEATURE_COUNT_ANGLE
TEST_P(EGLFeatureControlTest,FeatureCount)117 TEST_P(EGLFeatureControlTest, FeatureCount)
118 {
119 ANGLE_SKIP_TEST_IF(!initTest());
120 egl::Display *display = static_cast<egl::Display *>(mDisplay);
121 EGLAttrib value = -1;
122 EXPECT_EQ(static_cast<EGLBoolean>(EGL_TRUE),
123 eglQueryDisplayAttribANGLE(mDisplay, EGL_FEATURE_COUNT_ANGLE, &value));
124 EXPECT_EQ(display->getFeatures().size(), static_cast<size_t>(value));
125 ASSERT_EGL_SUCCESS();
126 }
127
testOverrideFeatures(FeatureNameModifier modifyName)128 void EGLFeatureControlTest::testOverrideFeatures(FeatureNameModifier modifyName)
129 {
130 ANGLE_SKIP_TEST_IF(!initTest());
131 egl::Display *display = static_cast<egl::Display *>(mDisplay);
132 angle::FeatureList features = display->getFeatures();
133
134 // Build lists of features to enable/disabled. Toggle features we know are ok to toggle based
135 // from this list.
136 std::vector<const char *> enabled;
137 std::vector<const char *> disabled;
138 std::vector<std::string> modifiedNameStorage;
139 std::vector<bool> shouldBe;
140 std::vector<std::string> testedFeatures = {
141 // Safe to toggle on GL
142 angle::GetFeatureName(angle::Feature::AddAndTrueToLoopCondition),
143 angle::GetFeatureName(angle::Feature::ClampFragDepth),
144 // Safe to toggle on GL and Vulkan
145 angle::GetFeatureName(angle::Feature::ClampPointSize),
146 // Safe to toggle on D3D
147 angle::GetFeatureName(angle::Feature::ZeroMaxLodWorkaround),
148 angle::GetFeatureName(angle::Feature::ExpandIntegerPowExpressions),
149 angle::GetFeatureName(angle::Feature::RewriteUnaryMinusOperator),
150 };
151
152 modifiedNameStorage.reserve(features.size());
153 shouldBe.reserve(features.size());
154
155 for (size_t i = 0; i < features.size(); i++)
156 {
157 modifiedNameStorage.push_back(modifyName(features[i]->name));
158
159 bool toggle = std::find(testedFeatures.begin(), testedFeatures.end(),
160 std::string(features[i]->name)) != testedFeatures.end();
161 if (features[i]->enabled ^ toggle)
162 {
163 enabled.push_back(modifiedNameStorage[i].c_str());
164 }
165 else
166 {
167 disabled.push_back(modifiedNameStorage[i].c_str());
168 }
169 // Save what we expect the feature status will be when checking later.
170 shouldBe.push_back(features[i]->enabled ^ toggle);
171 }
172 disabled.push_back(0);
173 enabled.push_back(0);
174
175 // Terminate the old display (we just used it to collect features)
176 eglTerminate(mDisplay);
177
178 // Create a new display with these overridden features.
179 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
180 GetParam().getRenderer(),
181 EGL_FEATURE_OVERRIDES_ENABLED_ANGLE,
182 reinterpret_cast<EGLAttrib>(enabled.data()),
183 EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
184 reinterpret_cast<EGLAttrib>(disabled.data()),
185 EGL_NONE};
186 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
187 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
188 ASSERT_EGL_SUCCESS();
189 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
190 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
191
192 // Check that all features have the correct status (even the ones we toggled).
193 for (size_t i = 0; i < features.size(); i++)
194 {
195 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
196 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
197 << modifiedNameStorage[i];
198 }
199 }
200
201 // Submit a list of features to override when creating the display with eglGetPlatformDisplay, and
202 // ensure that the features are correctly overridden.
TEST_P(EGLFeatureControlTest,OverrideFeatures)203 TEST_P(EGLFeatureControlTest, OverrideFeatures)
204 {
205 testOverrideFeatures([](const std::string &featureName) { return featureName; });
206 }
207
208 // Similar to OverrideFeatures, but ensures that camelCase variants of the name match as well.
TEST_P(EGLFeatureControlTest,OverrideFeaturesCamelCase)209 TEST_P(EGLFeatureControlTest, OverrideFeaturesCamelCase)
210 {
211 testOverrideFeatures(
212 [](const std::string &featureName) { return angle::ToCamelCase(featureName); });
213 }
214
215 // Similar to OverrideFeatures, but ensures wildcard matching works
TEST_P(EGLFeatureControlTest,OverrideFeaturesWildcard)216 TEST_P(EGLFeatureControlTest, OverrideFeaturesWildcard)
217 {
218 for (int j = 0; j < 2; j++)
219 {
220 const bool testEnableOverride = (j != 0);
221
222 ANGLE_SKIP_TEST_IF(!initTest());
223
224 egl::Display *display = static_cast<egl::Display *>(mDisplay);
225 angle::FeatureList features = display->getFeatures();
226
227 // Note that we don't use the broader 'prefer_*' here because
228 // prefer_monolithic_pipelines_over_libraries may affect other feature
229 // flags.
230 std::vector<const char *> featuresToOverride = {"prefer_d*", nullptr};
231
232 std::vector<std::string> featureNameStorage;
233 std::vector<bool> shouldBe;
234
235 shouldBe.reserve(features.size());
236 featureNameStorage.reserve(features.size());
237
238 for (size_t i = 0; i < features.size(); i++)
239 {
240 std::string featureName = std::string(features[i]->name);
241 std::transform(featureName.begin(), featureName.end(), featureName.begin(),
242 [](unsigned char c) { return std::tolower(c); });
243
244 const bool featureMatch = strncmp(featureName.c_str(), "preferd", 7) == 0;
245
246 std::optional<bool> overrideState;
247 if (featureMatch)
248 {
249 overrideState = testEnableOverride;
250 }
251
252 // Save what we expect the feature status will be when checking later.
253 shouldBe.push_back(overrideState.value_or(features[i]->enabled));
254 featureNameStorage.push_back(features[i]->name);
255 }
256
257 // Terminate the old display (we just used it to collect features)
258 eglTerminate(mDisplay);
259 mDisplay = nullptr;
260
261 // Create a new display with these overridden features.
262 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
263 testEnableOverride ? EGL_FEATURE_OVERRIDES_ENABLED_ANGLE
264 : EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
265 reinterpret_cast<EGLAttrib>(featuresToOverride.data()), EGL_NONE};
266 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
267 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
268 ASSERT_EGL_SUCCESS();
269 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
270 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
271
272 // Check that all features have the correct status (even the ones we toggled).
273 for (size_t i = 0; i < features.size(); i++)
274 {
275 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
276 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
277 << featureNameStorage[i];
278 }
279
280 // Clean up display for next iteration.
281 eglTerminate(mDisplay);
282 mDisplay = nullptr;
283 }
284 }
285
286 // Ensure that dependent features are affected properly by overrides
TEST_P(EGLFeatureControlTest,OverrideFeaturesDependent)287 TEST_P(EGLFeatureControlTest, OverrideFeaturesDependent)
288 {
289 ANGLE_SKIP_TEST_IF(!initTest());
290
291 egl::Display *display = static_cast<egl::Display *>(mDisplay);
292 angle::FeatureList features = display->getFeatures();
293
294 const std::vector<const char *> featuresDisabled = {
295 GetFeatureName(Feature::SupportsRenderpass2),
296 GetFeatureName(Feature::SupportsImage2dViewOf3d), nullptr};
297
298 const std::vector<const char *> featuresExpectDisabled = {
299 // Features we changed
300 GetFeatureName(Feature::SupportsRenderpass2),
301 GetFeatureName(Feature::SupportsImage2dViewOf3d),
302
303 // Features that must become disabled as a result of the above
304 GetFeatureName(Feature::SupportsDepthStencilResolve),
305 GetFeatureName(Feature::SupportsDepthStencilIndependentResolveNone),
306 GetFeatureName(Feature::SupportsSampler2dViewOf3d),
307 GetFeatureName(Feature::SupportsFragmentShadingRate),
308 };
309
310 std::vector<std::string> featureNameStorage;
311 std::vector<bool> shouldBe;
312
313 shouldBe.reserve(features.size());
314 featureNameStorage.reserve(features.size());
315
316 for (size_t i = 0; i < features.size(); i++)
317 {
318 bool featureMatch = false;
319 for (auto *ptr : featuresExpectDisabled)
320 {
321 if (strcmp(ptr, features[i]->name) == 0)
322 {
323 featureMatch = true;
324 break;
325 }
326 }
327
328 std::string featureName = std::string(features[i]->name);
329 std::transform(featureName.begin(), featureName.end(), featureName.begin(),
330 [](unsigned char c) { return std::tolower(c); });
331
332 // Save what we expect the feature status will be when checking later.
333 shouldBe.push_back(features[i]->enabled && !featureMatch);
334
335 // Store copy of the feature name string, in case we need to print for a test failure
336 featureNameStorage.push_back(features[i]->name);
337 }
338
339 // Terminate the old display (we just used it to collect features)
340 eglTerminate(mDisplay);
341
342 // Create a new display with these overridden features.
343 EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
344 EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
345 reinterpret_cast<EGLAttrib>(featuresDisabled.data()), EGL_NONE};
346 mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
347 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
348 ASSERT_EGL_SUCCESS();
349 ASSERT_NE(mDisplay, EGL_NO_DISPLAY);
350 ASSERT_EQ(eglInitialize(mDisplay, nullptr, nullptr), EGLBoolean(EGL_TRUE));
351
352 // Check that all features have the correct status (even the ones we toggled).
353 for (size_t i = 0; i < features.size(); i++)
354 {
355 EXPECT_STREQ(FeatureStatusToString(shouldBe[i]),
356 eglQueryStringiANGLE(mDisplay, EGL_FEATURE_STATUS_ANGLE, i))
357 << featureNameStorage[i];
358 }
359 }
360
361 ANGLE_INSTANTIATE_TEST(EGLFeatureControlTest,
362 WithNoFixture(ES2_D3D9()),
363 WithNoFixture(ES2_D3D11()),
364 WithNoFixture(ES2_OPENGL()),
365 WithNoFixture(ES2_VULKAN()),
366 WithNoFixture(ES3_D3D11()),
367 WithNoFixture(ES3_OPENGL()));
368