• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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