| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/shell/platform/windows/egl/manager.h" | |
| #include <vector> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/shell/platform/windows/egl/egl.h" | |
| namespace flutter { | |
| namespace egl { | |
| int Manager::instance_count_ = 0; | |
| std::unique_ptr<Manager> Manager::Create(bool enable_impeller) { | |
| std::unique_ptr<Manager> manager; | |
| manager.reset(new Manager(enable_impeller)); | |
| if (!manager->IsValid()) { | |
| return nullptr; | |
| } | |
| return std::move(manager); | |
| } | |
| Manager::Manager(bool enable_impeller) { | |
| ++instance_count_; | |
| if (!InitializeDisplay()) { | |
| return; | |
| } | |
| if (!InitializeConfig(enable_impeller)) { | |
| return; | |
| } | |
| if (!InitializeContexts()) { | |
| return; | |
| } | |
| is_valid_ = true; | |
| } | |
| Manager::~Manager() { | |
| CleanUp(); | |
| --instance_count_; | |
| } | |
| bool Manager::InitializeDisplay() { | |
| // 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+. | |
| const EGLint d3d11_display_attributes[] = { | |
| 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, | |
| EGL_NONE, | |
| }; | |
| // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature | |
| // Level 9_3. | |
| const EGLint d3d11_fl_9_3_display_attributes[] = { | |
| EGL_PLATFORM_ANGLE_TYPE_ANGLE, | |
| EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, | |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, | |
| 9, | |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, | |
| 3, | |
| EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, | |
| EGL_TRUE, | |
| EGL_NONE, | |
| }; | |
| // These attributes request D3D11 WARP (software rendering fallback) in case | |
| // hardware-backed D3D11 is unavailable. | |
| const EGLint d3d11_warp_display_attributes[] = { | |
| EGL_PLATFORM_ANGLE_TYPE_ANGLE, | |
| EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, | |
| EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, | |
| EGL_TRUE, | |
| EGL_NONE, | |
| }; | |
| std::vector<const EGLint*> display_attributes_configs = { | |
| d3d11_display_attributes, | |
| d3d11_fl_9_3_display_attributes, | |
| d3d11_warp_display_attributes, | |
| }; | |
| PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT = | |
| reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>( | |
| ::eglGetProcAddress("eglGetPlatformDisplayEXT")); | |
| if (!egl_get_platform_display_EXT) { | |
| LogEGLError("eglGetPlatformDisplayEXT not available"); | |
| return false; | |
| } | |
| // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature | |
| // Level 9_3 and finally D3D11 WARP. | |
| for (auto config : display_attributes_configs) { | |
| bool is_last = (config == display_attributes_configs.back()); | |
| display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE, | |
| EGL_DEFAULT_DISPLAY, config); | |
| if (display_ == EGL_NO_DISPLAY) { | |
| if (is_last) { | |
| LogEGLError("Failed to get a compatible EGLdisplay"); | |
| return false; | |
| } | |
| // Try the next config. | |
| continue; | |
| } | |
| if (::eglInitialize(display_, nullptr, nullptr) == EGL_FALSE) { | |
| if (is_last) { | |
| LogEGLError("Failed to initialize EGL via ANGLE"); | |
| return false; | |
| } | |
| // Try the next config. | |
| continue; | |
| } | |
| return true; | |
| } | |
| FML_UNREACHABLE(); | |
| } | |
| bool Manager::InitializeConfig(bool enable_impeller) { | |
| const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, | |
| EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, | |
| EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, | |
| EGL_NONE}; | |
| const EGLint impeller_config_attributes[] = { | |
| EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, | |
| EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8, | |
| EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE}; | |
| const EGLint impeller_config_attributes_no_msaa[] = { | |
| EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, | |
| EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8, | |
| EGL_NONE}; | |
| EGLBoolean result; | |
| EGLint num_config = 0; | |
| if (enable_impeller) { | |
| // First try the MSAA configuration. | |
| result = ::eglChooseConfig(display_, impeller_config_attributes, &config_, | |
| 1, &num_config); | |
| if (result == EGL_TRUE && num_config > 0) { | |
| return true; | |
| } | |
| // Next fall back to disabled MSAA. | |
| result = ::eglChooseConfig(display_, impeller_config_attributes_no_msaa, | |
| &config_, 1, &num_config); | |
| if (result == EGL_TRUE && num_config == 0) { | |
| return true; | |
| } | |
| } else { | |
| result = ::eglChooseConfig(display_, config_attributes, &config_, 1, | |
| &num_config); | |
| if (result == EGL_TRUE && num_config > 0) { | |
| return true; | |
| } | |
| } | |
| LogEGLError("Failed to choose EGL config"); | |
| return false; | |
| } | |
| bool Manager::InitializeContexts() { | |
| const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | |
| auto const render_context = | |
| ::eglCreateContext(display_, config_, EGL_NO_CONTEXT, context_attributes); | |
| if (render_context == EGL_NO_CONTEXT) { | |
| LogEGLError("Failed to create EGL render context"); | |
| return false; | |
| } | |
| auto const resource_context = | |
| ::eglCreateContext(display_, config_, render_context, context_attributes); | |
| if (resource_context == EGL_NO_CONTEXT) { | |
| LogEGLError("Failed to create EGL resource context"); | |
| return false; | |
| } | |
| render_context_ = std::make_unique<Context>(display_, render_context); | |
| resource_context_ = std::make_unique<Context>(display_, resource_context); | |
| return true; | |
| } | |
| bool Manager::InitializeDevice() { | |
| const auto query_display_attrib_EXT = | |
| reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>( | |
| ::eglGetProcAddress("eglQueryDisplayAttribEXT")); | |
| const auto query_device_attrib_EXT = | |
| reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>( | |
| ::eglGetProcAddress("eglQueryDeviceAttribEXT")); | |
| if (query_display_attrib_EXT == nullptr || | |
| query_device_attrib_EXT == nullptr) { | |
| return false; | |
| } | |
| EGLAttrib egl_device = 0; | |
| EGLAttrib angle_device = 0; | |
| auto result = query_display_attrib_EXT(display_, EGL_DEVICE_EXT, &egl_device); | |
| if (result != EGL_TRUE) { | |
| return false; | |
| } | |
| result = query_device_attrib_EXT(reinterpret_cast<EGLDeviceEXT>(egl_device), | |
| EGL_D3D11_DEVICE_ANGLE, &angle_device); | |
| if (result != EGL_TRUE) { | |
| return false; | |
| } | |
| resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device); | |
| return true; | |
| } | |
| void Manager::CleanUp() { | |
| EGLBoolean result = EGL_FALSE; | |
| // Needs to be reset before destroying the contexts. | |
| resolved_device_.Reset(); | |
| // Needs to be reset before destroying the EGLDisplay. | |
| render_context_.reset(); | |
| resource_context_.reset(); | |
| if (display_ != EGL_NO_DISPLAY) { | |
| // Display is reused between instances so only terminate display | |
| // if destroying last instance | |
| if (instance_count_ == 1) { | |
| ::eglTerminate(display_); | |
| } | |
| display_ = EGL_NO_DISPLAY; | |
| } | |
| } | |
| bool Manager::IsValid() const { | |
| return is_valid_; | |
| } | |
| std::unique_ptr<WindowSurface> Manager::CreateWindowSurface(HWND hwnd, | |
| size_t width, | |
| size_t height) { | |
| if (!hwnd || !is_valid_) { | |
| return nullptr; | |
| } | |
| // Disable ANGLE's automatic surface resizing and provide an explicit size. | |
| // The surface will need to be destroyed and re-created if the HWND is | |
| // resized. | |
| const EGLint surface_attributes[] = {EGL_FIXED_SIZE_ANGLE, | |
| EGL_TRUE, | |
| EGL_WIDTH, | |
| static_cast<EGLint>(width), | |
| EGL_HEIGHT, | |
| static_cast<EGLint>(height), | |
| EGL_NONE}; | |
| auto const surface = ::eglCreateWindowSurface( | |
| display_, config_, static_cast<EGLNativeWindowType>(hwnd), | |
| surface_attributes); | |
| if (surface == EGL_NO_SURFACE) { | |
| LogEGLError("Surface creation failed."); | |
| return nullptr; | |
| } | |
| return std::make_unique<WindowSurface>(display_, render_context_->GetHandle(), | |
| surface, width, height); | |
| } | |
| bool Manager::HasContextCurrent() { | |
| return ::eglGetCurrentContext() != EGL_NO_CONTEXT; | |
| } | |
| EGLSurface Manager::CreateSurfaceFromHandle(EGLenum handle_type, | |
| EGLClientBuffer handle, | |
| const EGLint* attributes) const { | |
| return ::eglCreatePbufferFromClientBuffer(display_, handle_type, handle, | |
| config_, attributes); | |
| } | |
| bool Manager::GetDevice(ID3D11Device** device) { | |
| if (!resolved_device_) { | |
| if (!InitializeDevice()) { | |
| return false; | |
| } | |
| } | |
| resolved_device_.CopyTo(device); | |
| return (resolved_device_ != nullptr); | |
| } | |
| Context* Manager::render_context() const { | |
| return render_context_.get(); | |
| } | |
| Context* Manager::resource_context() const { | |
| return resource_context_.get(); | |
| } | |
| } // namespace egl | |
| } // namespace flutter |