[Windows] Allow apps to prefer low power GPUs (#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
This commit is contained in:
parent
b0d95460e4
commit
6ea438f296
@ -17,6 +17,8 @@ FlutterEngine::FlutterEngine(const DartProject& project) {
|
||||
c_engine_properties.icu_data_path = project.icu_data_path().c_str();
|
||||
c_engine_properties.aot_library_path = project.aot_library_path().c_str();
|
||||
c_engine_properties.dart_entrypoint = project.dart_entrypoint().c_str();
|
||||
c_engine_properties.gpu_preference =
|
||||
static_cast<FlutterDesktopGpuPreference>(project.gpu_preference());
|
||||
|
||||
const std::vector<std::string>& entrypoint_args =
|
||||
project.dart_entrypoint_arguments();
|
||||
|
@ -10,6 +10,16 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// Configures how the Flutter engine selects a GPU.
|
||||
enum class GpuPreference {
|
||||
// No preference.
|
||||
NoPreference,
|
||||
// Prefer energy efficiency over performance, such as an integrated GPU.
|
||||
// This falls back to a high performance GPU if no low power GPU is
|
||||
// available.
|
||||
LowPowerPreference,
|
||||
};
|
||||
|
||||
// A set of Flutter and Dart assets used to initialize a Flutter engine.
|
||||
class DartProject {
|
||||
public:
|
||||
@ -71,6 +81,15 @@ class DartProject {
|
||||
return dart_entrypoint_arguments_;
|
||||
}
|
||||
|
||||
// Sets the GPU usage preference for flutter engine.
|
||||
void set_gpu_preference(GpuPreference gpu_preference) {
|
||||
gpu_preference_ = gpu_preference;
|
||||
}
|
||||
|
||||
// Returns the project's GPU preference.
|
||||
// Defaults to NoPreference.
|
||||
GpuPreference gpu_preference() const { return gpu_preference_; }
|
||||
|
||||
private:
|
||||
// Accessors for internals are private, so that they can be changed if more
|
||||
// flexible options for project structures are needed later without it
|
||||
@ -95,6 +114,8 @@ class DartProject {
|
||||
std::string dart_entrypoint_;
|
||||
// The list of arguments to pass through to the Dart entrypoint.
|
||||
std::vector<std::string> dart_entrypoint_arguments_;
|
||||
// The preference for GPU to be used by flutter engine.
|
||||
GpuPreference gpu_preference_ = GpuPreference::NoPreference;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
@ -14,19 +14,19 @@ namespace egl {
|
||||
|
||||
int Manager::instance_count_ = 0;
|
||||
|
||||
std::unique_ptr<Manager> Manager::Create() {
|
||||
std::unique_ptr<Manager> Manager::Create(GpuPreference gpu_preference) {
|
||||
std::unique_ptr<Manager> manager;
|
||||
manager.reset(new Manager());
|
||||
manager.reset(new Manager(gpu_preference));
|
||||
if (!manager->IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::move(manager);
|
||||
}
|
||||
|
||||
Manager::Manager() {
|
||||
Manager::Manager(GpuPreference gpu_preference) {
|
||||
++instance_count_;
|
||||
|
||||
if (!InitializeDisplay()) {
|
||||
if (!InitializeDisplay(gpu_preference)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -46,7 +46,40 @@ Manager::~Manager() {
|
||||
--instance_count_;
|
||||
}
|
||||
|
||||
bool Manager::InitializeDisplay() {
|
||||
bool Manager::InitializeDisplay(GpuPreference gpu_preference) {
|
||||
// If the request for a low power GPU is provided,
|
||||
// we will attempt to select GPU explicitly, via ANGLE extension
|
||||
// that allows to specify the GPU to use via LUID.
|
||||
std::optional<LUID> luid = std::nullopt;
|
||||
if (gpu_preference == GpuPreference::LowPowerPreference) {
|
||||
luid = GetLowPowerGpuLuid();
|
||||
}
|
||||
|
||||
// These are preferred display attributes and request ANGLE's D3D11
|
||||
// renderer (use only in case of valid LUID returned from above).
|
||||
const EGLint d3d11_display_attributes_with_luid[] = {
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
|
||||
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
|
||||
// enable ANGLE to automatically call the IDXGIDevice3::Trim method on
|
||||
// behalf of the application when it gets suspended.
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
|
||||
EGL_TRUE,
|
||||
|
||||
// This extension allows angle to render directly on a D3D swapchain
|
||||
// in the correct orientation on D3D11.
|
||||
EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
|
||||
EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
|
||||
|
||||
// Specify the LUID of the GPU to use.
|
||||
EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE,
|
||||
static_cast<EGLint>(luid.has_value() ? luid->HighPart : 0),
|
||||
EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE,
|
||||
static_cast<EGLint>(luid.has_value() ? luid->LowPart : 0),
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
// These are preferred display attributes and request ANGLE's D3D11
|
||||
// renderer. eglInitialize will only succeed with these attributes if the
|
||||
// hardware supports D3D11 Feature Level 10_0+.
|
||||
@ -92,11 +125,15 @@ bool Manager::InitializeDisplay() {
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
std::vector<const EGLint*> display_attributes_configs = {
|
||||
d3d11_display_attributes,
|
||||
d3d11_fl_9_3_display_attributes,
|
||||
d3d11_warp_display_attributes,
|
||||
};
|
||||
std::vector<const EGLint*> display_attributes_configs;
|
||||
|
||||
if (luid) {
|
||||
// If LUID value is present, obtain an adapter with that luid.
|
||||
display_attributes_configs.push_back(d3d11_display_attributes_with_luid);
|
||||
}
|
||||
display_attributes_configs.push_back(d3d11_display_attributes);
|
||||
display_attributes_configs.push_back(d3d11_fl_9_3_display_attributes);
|
||||
display_attributes_configs.push_back(d3d11_warp_display_attributes);
|
||||
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
|
||||
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
|
||||
@ -296,5 +333,33 @@ Context* Manager::resource_context() const {
|
||||
return resource_context_.get();
|
||||
}
|
||||
|
||||
std::optional<LUID> Manager::GetLowPowerGpuLuid() {
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory1> factory1 = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory6> factory6 = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter = nullptr;
|
||||
HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&factory1));
|
||||
if (FAILED(hr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
hr = factory1->QueryInterface(IID_PPV_ARGS(&factory6));
|
||||
if (FAILED(hr)) {
|
||||
// No support for IDXGIFactory6, so we will not use the selected GPU.
|
||||
// We will follow with the default ANGLE selection.
|
||||
return std::nullopt;
|
||||
}
|
||||
hr = factory6->EnumAdapterByGpuPreference(
|
||||
0, DXGI_GPU_PREFERENCE_MINIMUM_POWER, IID_PPV_ARGS(&adapter));
|
||||
if (FAILED(hr) || adapter == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
// Get the LUID of the adapter.
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
hr = adapter->GetDesc(&desc);
|
||||
if (FAILED(hr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional(desc.AdapterLuid);
|
||||
}
|
||||
|
||||
} // namespace egl
|
||||
} // namespace flutter
|
||||
|
@ -14,9 +14,12 @@
|
||||
|
||||
// Windows platform specific includes
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <windows.h>
|
||||
#include <wrl/client.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/shell/platform/windows/egl/context.h"
|
||||
@ -26,11 +29,16 @@
|
||||
namespace flutter {
|
||||
namespace egl {
|
||||
|
||||
enum class GpuPreference {
|
||||
NoPreference,
|
||||
LowPowerPreference,
|
||||
};
|
||||
|
||||
// A manager for initializing ANGLE correctly and using it to create and
|
||||
// destroy surfaces
|
||||
class Manager {
|
||||
public:
|
||||
static std::unique_ptr<Manager> Create();
|
||||
static std::unique_ptr<Manager> Create(GpuPreference gpu_preference);
|
||||
|
||||
virtual ~Manager();
|
||||
|
||||
@ -71,17 +79,19 @@ class Manager {
|
||||
// Get the EGL context used for async texture uploads.
|
||||
virtual Context* resource_context() const;
|
||||
|
||||
static std::optional<LUID> GetLowPowerGpuLuid();
|
||||
|
||||
protected:
|
||||
// Creates a new surface manager retaining reference to the passed-in target
|
||||
// for the lifetime of the manager.
|
||||
explicit Manager();
|
||||
explicit Manager(GpuPreference gpu_preference);
|
||||
|
||||
private:
|
||||
// Number of active instances of Manager
|
||||
static int instance_count_;
|
||||
|
||||
// Initialize the EGL display.
|
||||
bool InitializeDisplay();
|
||||
bool InitializeDisplay(GpuPreference gpu_preference);
|
||||
|
||||
// Initialize the EGL configs.
|
||||
bool InitializeConfig();
|
||||
|
@ -29,6 +29,9 @@ FlutterProjectBundle::FlutterProjectBundle(
|
||||
std::string(properties.dart_entrypoint_argv[i]));
|
||||
}
|
||||
|
||||
gpu_preference_ =
|
||||
static_cast<FlutterGpuPreference>(properties.gpu_preference);
|
||||
|
||||
// Resolve any relative paths.
|
||||
if (assets_path_.is_relative() || icu_path_.is_relative() ||
|
||||
(!aot_library_path_.empty() && aot_library_path_.is_relative())) {
|
||||
|
@ -17,6 +17,11 @@ namespace flutter {
|
||||
using UniqueAotDataPtr =
|
||||
std::unique_ptr<_FlutterEngineAOTData, FlutterEngineCollectAOTDataFnPtr>;
|
||||
|
||||
enum class FlutterGpuPreference {
|
||||
NoPreference,
|
||||
LowPowerPreference,
|
||||
};
|
||||
|
||||
// The data associated with a Flutter project needed to run it in an engine.
|
||||
class FlutterProjectBundle {
|
||||
public:
|
||||
@ -59,6 +64,9 @@ class FlutterProjectBundle {
|
||||
return dart_entrypoint_arguments_;
|
||||
}
|
||||
|
||||
// Returns the app's GPU preference.
|
||||
FlutterGpuPreference gpu_preference() const { return gpu_preference_; }
|
||||
|
||||
private:
|
||||
std::filesystem::path assets_path_;
|
||||
std::filesystem::path icu_path_;
|
||||
@ -74,6 +82,9 @@ class FlutterProjectBundle {
|
||||
|
||||
// Engine switches.
|
||||
std::vector<std::string> engine_switches_;
|
||||
|
||||
// App's GPU preference.
|
||||
FlutterGpuPreference gpu_preference_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
@ -194,7 +194,8 @@ FlutterWindowsEngine::FlutterWindowsEngine(
|
||||
enable_impeller_ = std::find(switches.begin(), switches.end(),
|
||||
"--enable-impeller=true") != switches.end();
|
||||
|
||||
egl_manager_ = egl::Manager::Create();
|
||||
egl_manager_ = egl::Manager::Create(
|
||||
static_cast<egl::GpuPreference>(project_->gpu_preference()));
|
||||
window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
|
||||
window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
|
||||
[](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
|
||||
|
@ -31,7 +31,7 @@ namespace {
|
||||
// An EGL manager that initializes EGL but fails to create surfaces.
|
||||
class HalfBrokenEGLManager : public egl::Manager {
|
||||
public:
|
||||
HalfBrokenEGLManager() : egl::Manager() {}
|
||||
HalfBrokenEGLManager() : egl::Manager(egl::GpuPreference::NoPreference) {}
|
||||
|
||||
std::unique_ptr<egl::WindowSurface>
|
||||
CreateWindowSurface(HWND hwnd, size_t width, size_t height) override {
|
||||
@ -422,6 +422,28 @@ TEST_F(WindowsTest, GetGraphicsAdapter) {
|
||||
ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
|
||||
}
|
||||
|
||||
TEST_F(WindowsTest, GetGraphicsAdapterWithLowPowerPreference) {
|
||||
std::optional<LUID> luid = egl::Manager::GetLowPowerGpuLuid();
|
||||
if (!luid) {
|
||||
GTEST_SKIP() << "Not able to find low power GPU, nothing to check.";
|
||||
}
|
||||
|
||||
auto& context = GetContext();
|
||||
WindowsConfigBuilder builder(context);
|
||||
builder.SetGpuPreference(FlutterDesktopGpuPreference::LowPowerPreference);
|
||||
ViewControllerPtr controller{builder.Run()};
|
||||
ASSERT_NE(controller, nullptr);
|
||||
auto view = FlutterDesktopViewControllerGetView(controller.get());
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
|
||||
dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
|
||||
ASSERT_NE(dxgi_adapter, nullptr);
|
||||
DXGI_ADAPTER_DESC desc{};
|
||||
ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
|
||||
ASSERT_EQ(desc.AdapterLuid.HighPart, luid->HighPart);
|
||||
ASSERT_EQ(desc.AdapterLuid.LowPart, luid->LowPart);
|
||||
}
|
||||
|
||||
// Implicit view has the implicit view ID.
|
||||
TEST_F(WindowsTest, PluginRegistrarGetImplicitView) {
|
||||
auto& context = GetContext();
|
||||
|
@ -35,6 +35,16 @@ typedef struct FlutterDesktopEngine* FlutterDesktopEngineRef;
|
||||
// The unique identifier for a view.
|
||||
typedef int64_t FlutterDesktopViewId;
|
||||
|
||||
// Configures how the Flutter engine selects a GPU.
|
||||
typedef enum {
|
||||
// No preference.
|
||||
NoPreference,
|
||||
// Prefer energy efficiency over performance, such as an integrated GPU.
|
||||
// This falls back to a high performance GPU if no low power GPU is
|
||||
// available.
|
||||
LowPowerPreference,
|
||||
} FlutterDesktopGpuPreference;
|
||||
|
||||
// Properties for configuring a Flutter engine instance.
|
||||
typedef struct {
|
||||
// The path to the flutter_assets folder for the application to be run.
|
||||
@ -68,6 +78,8 @@ typedef struct {
|
||||
// to FlutterDesktopEngineCreate.
|
||||
const char** dart_entrypoint_argv;
|
||||
|
||||
// GPU choice preference
|
||||
FlutterDesktopGpuPreference gpu_preference;
|
||||
} FlutterDesktopEngineProperties;
|
||||
|
||||
// ========== View Controller ==========
|
||||
|
@ -16,7 +16,7 @@ namespace egl {
|
||||
/// Mock for the |Manager| base class.
|
||||
class MockManager : public flutter::egl::Manager {
|
||||
public:
|
||||
MockManager() : Manager() {}
|
||||
MockManager() : Manager(flutter::egl::GpuPreference::NoPreference) {}
|
||||
|
||||
MOCK_METHOD(std::unique_ptr<flutter::egl::WindowSurface>,
|
||||
CreateWindowSurface,
|
||||
|
@ -38,6 +38,11 @@ void WindowsConfigBuilder::AddDartEntrypointArgument(std::string_view arg) {
|
||||
dart_entrypoint_arguments_.emplace_back(std::move(arg));
|
||||
}
|
||||
|
||||
void WindowsConfigBuilder::SetGpuPreference(
|
||||
FlutterDesktopGpuPreference gpu_preference) {
|
||||
gpu_preference_ = gpu_preference;
|
||||
}
|
||||
|
||||
FlutterDesktopEngineProperties WindowsConfigBuilder::GetEngineProperties()
|
||||
const {
|
||||
FlutterDesktopEngineProperties engine_properties = {};
|
||||
@ -63,6 +68,8 @@ FlutterDesktopEngineProperties WindowsConfigBuilder::GetEngineProperties()
|
||||
engine_properties.dart_entrypoint_argc = 0;
|
||||
}
|
||||
|
||||
engine_properties.gpu_preference = gpu_preference_;
|
||||
|
||||
return engine_properties;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,8 @@ class WindowsConfigBuilder {
|
||||
// Adds an argument to the Dart entrypoint arguments List<String>.
|
||||
void AddDartEntrypointArgument(std::string_view arg);
|
||||
|
||||
void SetGpuPreference(FlutterDesktopGpuPreference gpu_preference);
|
||||
|
||||
// Returns a configured and initialized engine.
|
||||
EnginePtr InitializeEngine() const;
|
||||
|
||||
@ -85,6 +87,9 @@ class WindowsConfigBuilder {
|
||||
std::string dart_entrypoint_;
|
||||
std::vector<std::string> dart_entrypoint_arguments_;
|
||||
|
||||
FlutterDesktopGpuPreference gpu_preference_ =
|
||||
FlutterDesktopGpuPreference::NoPreference;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(WindowsConfigBuilder);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user