Skip to content

Commit 6ea438f

Browse files
[Windows] Allow apps to prefer low power GPUs (flutter#162490)
…and make ANGLE backend to choose low-power GPU when this flag is provided. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent b0d9546 commit 6ea438f

File tree

12 files changed

+175
-16
lines changed

12 files changed

+175
-16
lines changed

engine/src/flutter/shell/platform/windows/client_wrapper/flutter_engine.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ FlutterEngine::FlutterEngine(const DartProject& project) {
1717
c_engine_properties.icu_data_path = project.icu_data_path().c_str();
1818
c_engine_properties.aot_library_path = project.aot_library_path().c_str();
1919
c_engine_properties.dart_entrypoint = project.dart_entrypoint().c_str();
20+
c_engine_properties.gpu_preference =
21+
static_cast<FlutterDesktopGpuPreference>(project.gpu_preference());
2022

2123
const std::vector<std::string>& entrypoint_args =
2224
project.dart_entrypoint_arguments();

engine/src/flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010

1111
namespace flutter {
1212

13+
// Configures how the Flutter engine selects a GPU.
14+
enum class GpuPreference {
15+
// No preference.
16+
NoPreference,
17+
// Prefer energy efficiency over performance, such as an integrated GPU.
18+
// This falls back to a high performance GPU if no low power GPU is
19+
// available.
20+
LowPowerPreference,
21+
};
22+
1323
// A set of Flutter and Dart assets used to initialize a Flutter engine.
1424
class DartProject {
1525
public:
@@ -71,6 +81,15 @@ class DartProject {
7181
return dart_entrypoint_arguments_;
7282
}
7383

84+
// Sets the GPU usage preference for flutter engine.
85+
void set_gpu_preference(GpuPreference gpu_preference) {
86+
gpu_preference_ = gpu_preference;
87+
}
88+
89+
// Returns the project's GPU preference.
90+
// Defaults to NoPreference.
91+
GpuPreference gpu_preference() const { return gpu_preference_; }
92+
7493
private:
7594
// Accessors for internals are private, so that they can be changed if more
7695
// flexible options for project structures are needed later without it
@@ -95,6 +114,8 @@ class DartProject {
95114
std::string dart_entrypoint_;
96115
// The list of arguments to pass through to the Dart entrypoint.
97116
std::vector<std::string> dart_entrypoint_arguments_;
117+
// The preference for GPU to be used by flutter engine.
118+
GpuPreference gpu_preference_ = GpuPreference::NoPreference;
98119
};
99120

100121
} // namespace flutter

engine/src/flutter/shell/platform/windows/egl/manager.cc

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ namespace egl {
1414

1515
int Manager::instance_count_ = 0;
1616

17-
std::unique_ptr<Manager> Manager::Create() {
17+
std::unique_ptr<Manager> Manager::Create(GpuPreference gpu_preference) {
1818
std::unique_ptr<Manager> manager;
19-
manager.reset(new Manager());
19+
manager.reset(new Manager(gpu_preference));
2020
if (!manager->IsValid()) {
2121
return nullptr;
2222
}
2323
return std::move(manager);
2424
}
2525

26-
Manager::Manager() {
26+
Manager::Manager(GpuPreference gpu_preference) {
2727
++instance_count_;
2828

29-
if (!InitializeDisplay()) {
29+
if (!InitializeDisplay(gpu_preference)) {
3030
return;
3131
}
3232

@@ -46,7 +46,40 @@ Manager::~Manager() {
4646
--instance_count_;
4747
}
4848

49-
bool Manager::InitializeDisplay() {
49+
bool Manager::InitializeDisplay(GpuPreference gpu_preference) {
50+
// If the request for a low power GPU is provided,
51+
// we will attempt to select GPU explicitly, via ANGLE extension
52+
// that allows to specify the GPU to use via LUID.
53+
std::optional<LUID> luid = std::nullopt;
54+
if (gpu_preference == GpuPreference::LowPowerPreference) {
55+
luid = GetLowPowerGpuLuid();
56+
}
57+
58+
// These are preferred display attributes and request ANGLE's D3D11
59+
// renderer (use only in case of valid LUID returned from above).
60+
const EGLint d3d11_display_attributes_with_luid[] = {
61+
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
62+
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
63+
64+
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
65+
// enable ANGLE to automatically call the IDXGIDevice3::Trim method on
66+
// behalf of the application when it gets suspended.
67+
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
68+
EGL_TRUE,
69+
70+
// This extension allows angle to render directly on a D3D swapchain
71+
// in the correct orientation on D3D11.
72+
EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
73+
EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
74+
75+
// Specify the LUID of the GPU to use.
76+
EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE,
77+
static_cast<EGLint>(luid.has_value() ? luid->HighPart : 0),
78+
EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE,
79+
static_cast<EGLint>(luid.has_value() ? luid->LowPart : 0),
80+
EGL_NONE,
81+
};
82+
5083
// These are preferred display attributes and request ANGLE's D3D11
5184
// renderer. eglInitialize will only succeed with these attributes if the
5285
// hardware supports D3D11 Feature Level 10_0+.
@@ -92,11 +125,15 @@ bool Manager::InitializeDisplay() {
92125
EGL_NONE,
93126
};
94127

95-
std::vector<const EGLint*> display_attributes_configs = {
96-
d3d11_display_attributes,
97-
d3d11_fl_9_3_display_attributes,
98-
d3d11_warp_display_attributes,
99-
};
128+
std::vector<const EGLint*> display_attributes_configs;
129+
130+
if (luid) {
131+
// If LUID value is present, obtain an adapter with that luid.
132+
display_attributes_configs.push_back(d3d11_display_attributes_with_luid);
133+
}
134+
display_attributes_configs.push_back(d3d11_display_attributes);
135+
display_attributes_configs.push_back(d3d11_fl_9_3_display_attributes);
136+
display_attributes_configs.push_back(d3d11_warp_display_attributes);
100137

101138
PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
102139
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
@@ -296,5 +333,33 @@ Context* Manager::resource_context() const {
296333
return resource_context_.get();
297334
}
298335

336+
std::optional<LUID> Manager::GetLowPowerGpuLuid() {
337+
Microsoft::WRL::ComPtr<IDXGIFactory1> factory1 = nullptr;
338+
Microsoft::WRL::ComPtr<IDXGIFactory6> factory6 = nullptr;
339+
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter = nullptr;
340+
HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&factory1));
341+
if (FAILED(hr)) {
342+
return std::nullopt;
343+
}
344+
hr = factory1->QueryInterface(IID_PPV_ARGS(&factory6));
345+
if (FAILED(hr)) {
346+
// No support for IDXGIFactory6, so we will not use the selected GPU.
347+
// We will follow with the default ANGLE selection.
348+
return std::nullopt;
349+
}
350+
hr = factory6->EnumAdapterByGpuPreference(
351+
0, DXGI_GPU_PREFERENCE_MINIMUM_POWER, IID_PPV_ARGS(&adapter));
352+
if (FAILED(hr) || adapter == nullptr) {
353+
return std::nullopt;
354+
}
355+
// Get the LUID of the adapter.
356+
DXGI_ADAPTER_DESC desc;
357+
hr = adapter->GetDesc(&desc);
358+
if (FAILED(hr)) {
359+
return std::nullopt;
360+
}
361+
return std::make_optional(desc.AdapterLuid);
362+
}
363+
299364
} // namespace egl
300365
} // namespace flutter

engine/src/flutter/shell/platform/windows/egl/manager.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414

1515
// Windows platform specific includes
1616
#include <d3d11.h>
17+
#include <dxgi.h>
18+
#include <dxgi1_6.h>
1719
#include <windows.h>
1820
#include <wrl/client.h>
1921
#include <memory>
22+
#include <optional>
2023

2124
#include "flutter/fml/macros.h"
2225
#include "flutter/shell/platform/windows/egl/context.h"
@@ -26,11 +29,16 @@
2629
namespace flutter {
2730
namespace egl {
2831

32+
enum class GpuPreference {
33+
NoPreference,
34+
LowPowerPreference,
35+
};
36+
2937
// A manager for initializing ANGLE correctly and using it to create and
3038
// destroy surfaces
3139
class Manager {
3240
public:
33-
static std::unique_ptr<Manager> Create();
41+
static std::unique_ptr<Manager> Create(GpuPreference gpu_preference);
3442

3543
virtual ~Manager();
3644

@@ -71,17 +79,19 @@ class Manager {
7179
// Get the EGL context used for async texture uploads.
7280
virtual Context* resource_context() const;
7381

82+
static std::optional<LUID> GetLowPowerGpuLuid();
83+
7484
protected:
7585
// Creates a new surface manager retaining reference to the passed-in target
7686
// for the lifetime of the manager.
77-
explicit Manager();
87+
explicit Manager(GpuPreference gpu_preference);
7888

7989
private:
8090
// Number of active instances of Manager
8191
static int instance_count_;
8292

8393
// Initialize the EGL display.
84-
bool InitializeDisplay();
94+
bool InitializeDisplay(GpuPreference gpu_preference);
8595

8696
// Initialize the EGL configs.
8797
bool InitializeConfig();

engine/src/flutter/shell/platform/windows/flutter_project_bundle.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ FlutterProjectBundle::FlutterProjectBundle(
2929
std::string(properties.dart_entrypoint_argv[i]));
3030
}
3131

32+
gpu_preference_ =
33+
static_cast<FlutterGpuPreference>(properties.gpu_preference);
34+
3235
// Resolve any relative paths.
3336
if (assets_path_.is_relative() || icu_path_.is_relative() ||
3437
(!aot_library_path_.empty() && aot_library_path_.is_relative())) {

engine/src/flutter/shell/platform/windows/flutter_project_bundle.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ namespace flutter {
1717
using UniqueAotDataPtr =
1818
std::unique_ptr<_FlutterEngineAOTData, FlutterEngineCollectAOTDataFnPtr>;
1919

20+
enum class FlutterGpuPreference {
21+
NoPreference,
22+
LowPowerPreference,
23+
};
24+
2025
// The data associated with a Flutter project needed to run it in an engine.
2126
class FlutterProjectBundle {
2227
public:
@@ -59,6 +64,9 @@ class FlutterProjectBundle {
5964
return dart_entrypoint_arguments_;
6065
}
6166

67+
// Returns the app's GPU preference.
68+
FlutterGpuPreference gpu_preference() const { return gpu_preference_; }
69+
6270
private:
6371
std::filesystem::path assets_path_;
6472
std::filesystem::path icu_path_;
@@ -74,6 +82,9 @@ class FlutterProjectBundle {
7482

7583
// Engine switches.
7684
std::vector<std::string> engine_switches_;
85+
86+
// App's GPU preference.
87+
FlutterGpuPreference gpu_preference_;
7788
};
7889

7990
} // namespace flutter

engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ FlutterWindowsEngine::FlutterWindowsEngine(
194194
enable_impeller_ = std::find(switches.begin(), switches.end(),
195195
"--enable-impeller=true") != switches.end();
196196

197-
egl_manager_ = egl::Manager::Create();
197+
egl_manager_ = egl::Manager::Create(
198+
static_cast<egl::GpuPreference>(project_->gpu_preference()));
198199
window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
199200
window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
200201
[](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,

engine/src/flutter/shell/platform/windows/flutter_windows_unittests.cc

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace {
3131
// An EGL manager that initializes EGL but fails to create surfaces.
3232
class HalfBrokenEGLManager : public egl::Manager {
3333
public:
34-
HalfBrokenEGLManager() : egl::Manager() {}
34+
HalfBrokenEGLManager() : egl::Manager(egl::GpuPreference::NoPreference) {}
3535

3636
std::unique_ptr<egl::WindowSurface>
3737
CreateWindowSurface(HWND hwnd, size_t width, size_t height) override {
@@ -422,6 +422,28 @@ TEST_F(WindowsTest, GetGraphicsAdapter) {
422422
ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
423423
}
424424

425+
TEST_F(WindowsTest, GetGraphicsAdapterWithLowPowerPreference) {
426+
std::optional<LUID> luid = egl::Manager::GetLowPowerGpuLuid();
427+
if (!luid) {
428+
GTEST_SKIP() << "Not able to find low power GPU, nothing to check.";
429+
}
430+
431+
auto& context = GetContext();
432+
WindowsConfigBuilder builder(context);
433+
builder.SetGpuPreference(FlutterDesktopGpuPreference::LowPowerPreference);
434+
ViewControllerPtr controller{builder.Run()};
435+
ASSERT_NE(controller, nullptr);
436+
auto view = FlutterDesktopViewControllerGetView(controller.get());
437+
438+
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
439+
dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
440+
ASSERT_NE(dxgi_adapter, nullptr);
441+
DXGI_ADAPTER_DESC desc{};
442+
ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
443+
ASSERT_EQ(desc.AdapterLuid.HighPart, luid->HighPart);
444+
ASSERT_EQ(desc.AdapterLuid.LowPart, luid->LowPart);
445+
}
446+
425447
// Implicit view has the implicit view ID.
426448
TEST_F(WindowsTest, PluginRegistrarGetImplicitView) {
427449
auto& context = GetContext();

engine/src/flutter/shell/platform/windows/public/flutter_windows.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ typedef struct FlutterDesktopEngine* FlutterDesktopEngineRef;
3535
// The unique identifier for a view.
3636
typedef int64_t FlutterDesktopViewId;
3737

38+
// Configures how the Flutter engine selects a GPU.
39+
typedef enum {
40+
// No preference.
41+
NoPreference,
42+
// Prefer energy efficiency over performance, such as an integrated GPU.
43+
// This falls back to a high performance GPU if no low power GPU is
44+
// available.
45+
LowPowerPreference,
46+
} FlutterDesktopGpuPreference;
47+
3848
// Properties for configuring a Flutter engine instance.
3949
typedef struct {
4050
// The path to the flutter_assets folder for the application to be run.
@@ -68,6 +78,8 @@ typedef struct {
6878
// to FlutterDesktopEngineCreate.
6979
const char** dart_entrypoint_argv;
7080

81+
// GPU choice preference
82+
FlutterDesktopGpuPreference gpu_preference;
7183
} FlutterDesktopEngineProperties;
7284

7385
// ========== View Controller ==========

engine/src/flutter/shell/platform/windows/testing/egl/mock_manager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace egl {
1616
/// Mock for the |Manager| base class.
1717
class MockManager : public flutter::egl::Manager {
1818
public:
19-
MockManager() : Manager() {}
19+
MockManager() : Manager(flutter::egl::GpuPreference::NoPreference) {}
2020

2121
MOCK_METHOD(std::unique_ptr<flutter::egl::WindowSurface>,
2222
CreateWindowSurface,

0 commit comments

Comments
 (0)