xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLFeatureControlTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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