diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2630a59426c63..cc529b958e279 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2227,8 +2227,11 @@ FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.h FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32_unittests.cc FILE: ../../../flutter/shell/platform/windows/event_watcher_win32.cc FILE: ../../../flutter/shell/platform/windows/event_watcher_win32.h -FILE: ../../../flutter/shell/platform/windows/external_texture_gl.cc -FILE: ../../../flutter/shell/platform/windows/external_texture_gl.h +FILE: ../../../flutter/shell/platform/windows/external_texture.h +FILE: ../../../flutter/shell/platform/windows/external_texture_d3d.cc +FILE: ../../../flutter/shell/platform/windows/external_texture_d3d.h +FILE: ../../../flutter/shell/platform/windows/external_texture_pixelbuffer.cc +FILE: ../../../flutter/shell/platform/windows/external_texture_pixelbuffer.h FILE: ../../../flutter/shell/platform/windows/flutter_key_map.cc FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_win32.cc FILE: ../../../flutter/shell/platform/windows/flutter_platform_node_delegate_win32.h diff --git a/shell/platform/common/client_wrapper/core_implementations.cc b/shell/platform/common/client_wrapper/core_implementations.cc index 1412db7c939f2..e90b26cd4d6e5 100644 --- a/shell/platform/common/client_wrapper/core_implementations.cc +++ b/shell/platform/common/client_wrapper/core_implementations.cc @@ -157,25 +157,37 @@ TextureRegistrarImpl::TextureRegistrarImpl( TextureRegistrarImpl::~TextureRegistrarImpl() = default; int64_t TextureRegistrarImpl::RegisterTexture(TextureVariant* texture) { + FlutterDesktopTextureInfo info = {}; if (auto pixel_buffer_texture = std::get_if(texture)) { - FlutterDesktopTextureInfo info = {}; info.type = kFlutterDesktopPixelBufferTexture; info.pixel_buffer_config.user_data = pixel_buffer_texture; info.pixel_buffer_config.callback = [](size_t width, size_t height, void* user_data) -> const FlutterDesktopPixelBuffer* { auto texture = static_cast(user_data); - auto buffer = texture->CopyPixelBuffer(width, height); - return buffer; + return texture->CopyPixelBuffer(width, height); }; - - int64_t texture_id = FlutterDesktopTextureRegistrarRegisterExternalTexture( - texture_registrar_ref_, &info); - return texture_id; + } else if (auto gpu_surface_texture = + std::get_if(texture)) { + info.type = kFlutterDesktopGpuSurfaceTexture; + info.gpu_surface_config.struct_size = + sizeof(FlutterDesktopGpuSurfaceTextureConfig); + info.gpu_surface_config.type = gpu_surface_texture->surface_type(); + info.gpu_surface_config.user_data = gpu_surface_texture; + info.gpu_surface_config.callback = + [](size_t width, size_t height, + void* user_data) -> const FlutterDesktopGpuSurfaceDescriptor* { + auto texture = static_cast(user_data); + return texture->ObtainDescriptor(width, height); + }; + } else { + std::cerr << "Attempting to register unknown texture variant." << std::endl; + return -1; } - std::cerr << "Attempting to register unknown texture variant." << std::endl; - return -1; + int64_t texture_id = FlutterDesktopTextureRegistrarRegisterExternalTexture( + texture_registrar_ref_, &info); + return texture_id; } // namespace flutter bool TextureRegistrarImpl::MarkTextureFrameAvailable(int64_t texture_id) { diff --git a/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h b/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h index 6aa2c6b1d818f..7a2078f13daad 100644 --- a/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +++ b/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h @@ -42,10 +42,40 @@ class PixelBufferTexture { const CopyBufferCallback copy_buffer_callback_; }; +// A GPU surface-based texture. +class GpuSurfaceTexture { + public: + // A callback used for retrieving surface descriptors. + typedef std::function< + const FlutterDesktopGpuSurfaceDescriptor*(size_t width, size_t height)> + ObtainDescriptorCallback; + + GpuSurfaceTexture(FlutterDesktopGpuSurfaceType surface_type, + ObtainDescriptorCallback obtain_descriptor_callback) + : surface_type_(surface_type), + obtain_descriptor_callback_(obtain_descriptor_callback) {} + + // Returns the callback-provided FlutterDesktopGpuSurfaceDescriptor that + // contains the surface handle. The intended surface size is specified by + // |width| and |height|. + const FlutterDesktopGpuSurfaceDescriptor* ObtainDescriptor( + size_t width, + size_t height) const { + return obtain_descriptor_callback_(width, height); + } + + // Gets the surface type. + FlutterDesktopGpuSurfaceType surface_type() const { return surface_type_; } + + private: + const FlutterDesktopGpuSurfaceType surface_type_; + const ObtainDescriptorCallback obtain_descriptor_callback_; +}; + // The available texture variants. // Only PixelBufferTexture is currently implemented. // Other variants are expected to be added in the future. -typedef std::variant TextureVariant; +typedef std::variant TextureVariant; // An object keeping track of external textures. // diff --git a/shell/platform/common/public/flutter_texture_registrar.h b/shell/platform/common/public/flutter_texture_registrar.h index 711619e353609..45921613833e3 100644 --- a/shell/platform/common/public/flutter_texture_registrar.h +++ b/shell/platform/common/public/flutter_texture_registrar.h @@ -23,9 +23,35 @@ typedef struct FlutterDesktopTextureRegistrar* // Additional types may be added in the future. typedef enum { // A Pixel buffer-based texture. - kFlutterDesktopPixelBufferTexture + kFlutterDesktopPixelBufferTexture, + // A platform-specific GPU surface-backed texture. + kFlutterDesktopGpuSurfaceTexture } FlutterDesktopTextureType; +// Supported GPU surface types. +typedef enum { + // Uninitialized. + kFlutterDesktopGpuSurfaceTypeNone, + // A DXGI shared texture handle (Windows only). + // See + // https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiresource-getsharedhandle + kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle, + // A |ID3D11Texture2D| (Windows only). + kFlutterDesktopGpuSurfaceTypeD3d11Texture2D +} FlutterDesktopGpuSurfaceType; + +// Supported pixel formats. +typedef enum { + // Uninitialized. + kFlutterDesktopPixelFormatNone, + // Represents a 32-bit RGBA color format with 8 bits each for red, green, blue + // and alpha. + kFlutterDesktopPixelFormatRGBA8888, + // Represents a 32-bit BGRA color format with 8 bits each for blue, green, red + // and alpha. + kFlutterDesktopPixelFormatBGRA8888 +} FlutterDesktopPixelFormat; + // An image buffer object. typedef struct { // The pixel data buffer. @@ -40,20 +66,66 @@ typedef struct { void* release_context; } FlutterDesktopPixelBuffer; +// A GPU surface descriptor. +typedef struct { + // The size of this struct. Must be + // sizeof(FlutterDesktopGpuSurfaceDescriptor). + size_t struct_size; + // The surface handle. The expected type depends on the + // |FlutterDesktopGpuSurfaceType|. + // + // Provide a |ID3D11Texture2D*| when using + // |kFlutterDesktopGpuSurfaceTypeD3d11Texture2D| or a |HANDLE| when using + // |kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle|. + // + // The referenced resource needs to stay valid until it has been opened by + // Flutter. Consider incrementing the resource's reference count in the + // |FlutterDesktopGpuSurfaceTextureCallback| and registering a + // |release_callback| for decrementing the reference count once it has been + // opened. + void* handle; + // The physical width. + size_t width; + // The physical height. + size_t height; + // The visible width. + // It might be less or equal to the physical |width|. + size_t visible_width; + // The visible height. + // It might be less or equal to the physical |height|. + size_t visible_height; + // The pixel format which might be optional depending on the surface type. + FlutterDesktopPixelFormat format; + // An optional callback that gets invoked when the |handle| has been opened. + void (*release_callback)(void* release_context); + // Opaque data passed to |release_callback|. + void* release_context; +} FlutterDesktopGpuSurfaceDescriptor; + // The pixel buffer copy callback definition provided to // the Flutter engine to copy the texture. // It is invoked with the intended surface size specified by |width| and -// |height| and the |user_data| held by FlutterDesktopPixelBufferTextureConfig. +// |height| and the |user_data| held by +// |FlutterDesktopPixelBufferTextureConfig|. // // As this is usually called from the render thread, the callee must take // care of proper synchronization. It also needs to be ensured that the -// returned FlutterDesktopPixelBuffer isn't released prior to unregistering +// returned |FlutterDesktopPixelBuffer| isn't released prior to unregistering // the corresponding texture. typedef const FlutterDesktopPixelBuffer* ( *FlutterDesktopPixelBufferTextureCallback)(size_t width, size_t height, void* user_data); +// The GPU surface callback definition provided to the Flutter engine to obtain +// the surface. It is invoked with the intended surface size specified by +// |width| and |height| and the |user_data| held by +// |FlutterDesktopGpuSurfaceTextureConfig|. +typedef const FlutterDesktopGpuSurfaceDescriptor* ( + *FlutterDesktopGpuSurfaceTextureCallback)(size_t width, + size_t height, + void* user_data); + // An object used to configure pixel buffer textures. typedef struct { // The callback used by the engine to copy the pixel buffer object. @@ -62,10 +134,25 @@ typedef struct { void* user_data; } FlutterDesktopPixelBufferTextureConfig; +// An object used to configure GPU-surface textures. +typedef struct { + // The size of this struct. Must be + // sizeof(FlutterDesktopGpuSurfaceTextureConfig). + size_t struct_size; + // The concrete surface type (e.g. + // |kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle|) + FlutterDesktopGpuSurfaceType type; + // The callback used by the engine to obtain the surface descriptor. + FlutterDesktopGpuSurfaceTextureCallback callback; + // Opaque data that will get passed to the provided |callback|. + void* user_data; +} FlutterDesktopGpuSurfaceTextureConfig; + typedef struct { FlutterDesktopTextureType type; union { FlutterDesktopPixelBufferTextureConfig pixel_buffer_config; + FlutterDesktopGpuSurfaceTextureConfig gpu_surface_config; }; } FlutterDesktopTextureInfo; diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 670fa5f3c9dfc..9672e7eeb7c1d 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -59,8 +59,11 @@ source_set("flutter_windows_source") { "dpi_utils_win32.h", "event_watcher_win32.cc", "event_watcher_win32.h", - "external_texture_gl.cc", - "external_texture_gl.h", + "external_texture.h", + "external_texture_d3d.cc", + "external_texture_d3d.h", + "external_texture_pixelbuffer.cc", + "external_texture_pixelbuffer.h", "flutter_key_map.cc", "flutter_platform_node_delegate_win32.cc", "flutter_platform_node_delegate_win32.h", diff --git a/shell/platform/windows/angle_surface_manager.cc b/shell/platform/windows/angle_surface_manager.cc index 336fb71e08e20..8f8daf02ddd54 100644 --- a/shell/platform/windows/angle_surface_manager.cc +++ b/shell/platform/windows/angle_surface_manager.cc @@ -174,6 +174,9 @@ bool AngleSurfaceManager::Initialize() { void AngleSurfaceManager::CleanUp() { EGLBoolean result = EGL_FALSE; + // Needs to be reset before destroying the EGLContext. + resolved_device_.Reset(); + if (egl_display_ != EGL_NO_DISPLAY && egl_context_ != EGL_NO_CONTEXT) { result = eglDestroyContext(egl_display_, egl_context_); egl_context_ = EGL_NO_CONTEXT; @@ -288,4 +291,44 @@ EGLBoolean AngleSurfaceManager::SwapBuffers() { return (eglSwapBuffers(egl_display_, render_surface_)); } +EGLSurface AngleSurfaceManager::CreateSurfaceFromHandle( + EGLenum handle_type, + EGLClientBuffer handle, + const EGLint* attributes) const { + return eglCreatePbufferFromClientBuffer(egl_display_, handle_type, handle, + egl_config_, attributes); +} + +bool AngleSurfaceManager::GetDevice(ID3D11Device** device) { + using Microsoft::WRL::ComPtr; + + if (!resolved_device_) { + PFNEGLQUERYDISPLAYATTRIBEXTPROC egl_query_display_attrib_EXT = + reinterpret_cast( + eglGetProcAddress("eglQueryDisplayAttribEXT")); + + PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT = + reinterpret_cast( + eglGetProcAddress("eglQueryDeviceAttribEXT")); + + if (!egl_query_display_attrib_EXT || !egl_query_device_attrib_EXT) { + return false; + } + + EGLAttrib egl_device = 0; + EGLAttrib angle_device = 0; + if (egl_query_display_attrib_EXT(egl_display_, EGL_DEVICE_EXT, + &egl_device) == EGL_TRUE) { + if (egl_query_device_attrib_EXT( + reinterpret_cast(egl_device), + EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) { + resolved_device_ = reinterpret_cast(angle_device); + } + } + } + + resolved_device_.CopyTo(device); + return (resolved_device_ != nullptr); +} + } // namespace flutter diff --git a/shell/platform/windows/angle_surface_manager.h b/shell/platform/windows/angle_surface_manager.h index 119f57d3e4f78..3e9f21262babf 100644 --- a/shell/platform/windows/angle_surface_manager.h +++ b/shell/platform/windows/angle_surface_manager.h @@ -13,7 +13,9 @@ #include // Windows platform specific includes +#include #include +#include #include #include "window_binding_handler.h" @@ -69,15 +71,25 @@ class AngleSurfaceManager { // not null. EGLBoolean SwapBuffers(); - private: - bool Initialize(); - void CleanUp(); + // Creates a |EGLSurface| from the provided handle. + EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, + EGLClientBuffer handle, + const EGLint* attributes) const; + + // Gets the |EGLDisplay|. + EGLDisplay egl_display() const { return egl_display_; }; + + // Gets the |ID3D11Device| chosen by ANGLE. + bool GetDevice(ID3D11Device** device); private: // Creates a new surface manager retaining reference to the passed-in target // for the lifetime of the manager. AngleSurfaceManager(); + bool Initialize(); + void CleanUp(); + // Attempts to initialize EGL using ANGLE. bool InitializeEGL( PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT, @@ -108,6 +120,9 @@ class AngleSurfaceManager { EGLint surface_width_ = 0; EGLint surface_height_ = 0; + // The current D3D device. + Microsoft::WRL::ComPtr resolved_device_; + // Number of active instances of AngleSurfaceManager static int instance_count_; }; diff --git a/shell/platform/windows/external_texture.h b/shell/platform/windows/external_texture.h new file mode 100644 index 0000000000000..be51f0bdf6487 --- /dev/null +++ b/shell/platform/windows/external_texture.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" + +#include +#include + +namespace flutter { + +typedef void (*glGenTexturesProc)(GLsizei n, GLuint* textures); +typedef void (*glDeleteTexturesProc)(GLsizei n, const GLuint* textures); +typedef void (*glBindTextureProc)(GLenum target, GLuint texture); +typedef void (*glTexParameteriProc)(GLenum target, GLenum pname, GLint param); +typedef void (*glTexImage2DProc)(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* data); + +// A struct containing pointers to resolved gl* functions. +struct GlProcs { + glGenTexturesProc glGenTextures; + glDeleteTexturesProc glDeleteTextures; + glBindTextureProc glBindTexture; + glTexParameteriProc glTexParameteri; + glTexImage2DProc glTexImage2D; + bool valid; +}; + +// Abstract external texture. +class ExternalTexture { + public: + virtual ~ExternalTexture() = default; + + // Returns the unique id of this texture. + int64_t texture_id() const { return reinterpret_cast(this); }; + + // Attempts to populate the specified |opengl_texture| with texture details + // such as the name, width, height and the pixel format. + // Returns true on success. + virtual bool PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_H_ diff --git a/shell/platform/windows/external_texture_d3d.cc b/shell/platform/windows/external_texture_d3d.cc new file mode 100644 index 0000000000000..7a4e0e2d45af3 --- /dev/null +++ b/shell/platform/windows/external_texture_d3d.cc @@ -0,0 +1,122 @@ +// 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/external_texture_d3d.h" + +#include +#include +#include + +#include "flutter/shell/platform/embedder/embedder_struct_macros.h" + +namespace flutter { + +ExternalTextureD3d::ExternalTextureD3d( + FlutterDesktopGpuSurfaceType type, + const FlutterDesktopGpuSurfaceTextureCallback texture_callback, + void* user_data, + const AngleSurfaceManager* surface_manager, + const GlProcs& gl_procs) + : type_(type), + texture_callback_(texture_callback), + user_data_(user_data), + surface_manager_(surface_manager), + gl_(gl_procs) {} + +ExternalTextureD3d::~ExternalTextureD3d() { + ReleaseImage(); + + if (gl_texture_ != 0) { + gl_.glDeleteTextures(1, &gl_texture_); + } +} + +bool ExternalTextureD3d::PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) { + const FlutterDesktopGpuSurfaceDescriptor* descriptor = + texture_callback_(width, height, user_data_); + + if (!CreateOrUpdateTexture(descriptor)) { + return false; + } + + // Populate the texture object used by the engine. + opengl_texture->target = GL_TEXTURE_2D; + opengl_texture->name = gl_texture_; + opengl_texture->format = GL_RGBA; + opengl_texture->destruction_callback = nullptr; + opengl_texture->user_data = nullptr; + opengl_texture->width = SAFE_ACCESS(descriptor, visible_width, 0); + opengl_texture->height = SAFE_ACCESS(descriptor, visible_height, 0); + + return true; +} + +void ExternalTextureD3d::ReleaseImage() { + if (egl_surface_ != EGL_NO_SURFACE) { + eglReleaseTexImage(surface_manager_->egl_display(), egl_surface_, + EGL_BACK_BUFFER); + eglDestroySurface(surface_manager_->egl_display(), egl_surface_); + egl_surface_ = EGL_NO_SURFACE; + } +} + +bool ExternalTextureD3d::CreateOrUpdateTexture( + const FlutterDesktopGpuSurfaceDescriptor* descriptor) { + if (descriptor == nullptr || + SAFE_ACCESS(descriptor, handle, nullptr) == nullptr) { + ReleaseImage(); + return false; + } + + if (gl_texture_ == 0) { + gl_.glGenTextures(1, &gl_texture_); + + gl_.glBindTexture(GL_TEXTURE_2D, gl_texture_); + gl_.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + gl_.glBindTexture(GL_TEXTURE_2D, gl_texture_); + } + + auto handle = SAFE_ACCESS(descriptor, handle, nullptr); + if (handle != last_surface_handle_) { + ReleaseImage(); + + EGLint attributes[] = { + EGL_WIDTH, + static_cast(SAFE_ACCESS(descriptor, width, 0)), + EGL_HEIGHT, + static_cast(SAFE_ACCESS(descriptor, height, 0)), + EGL_TEXTURE_TARGET, + EGL_TEXTURE_2D, + EGL_TEXTURE_FORMAT, + EGL_TEXTURE_RGBA, // always EGL_TEXTURE_RGBA + EGL_NONE}; + + egl_surface_ = surface_manager_->CreateSurfaceFromHandle( + (type_ == kFlutterDesktopGpuSurfaceTypeD3d11Texture2D) + ? EGL_D3D_TEXTURE_ANGLE + : EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + handle, attributes); + + if (egl_surface_ == EGL_NO_SURFACE || + eglBindTexImage(surface_manager_->egl_display(), egl_surface_, + EGL_BACK_BUFFER) == EGL_FALSE) { + std::cerr << "Binding D3D surface failed." << std::endl; + } + last_surface_handle_ = handle; + } + + auto release_callback = SAFE_ACCESS(descriptor, release_callback, nullptr); + if (release_callback) { + release_callback(SAFE_ACCESS(descriptor, release_context, nullptr)); + } + return egl_surface_ != EGL_NO_SURFACE; +} + +} // namespace flutter diff --git a/shell/platform/windows/external_texture_d3d.h b/shell/platform/windows/external_texture_d3d.h new file mode 100644 index 0000000000000..771c7cb5936ea --- /dev/null +++ b/shell/platform/windows/external_texture_d3d.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_D3D_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_D3D_H_ + +#include + +#include + +#include "flutter/shell/platform/common/public/flutter_texture_registrar.h" +#include "flutter/shell/platform/windows/angle_surface_manager.h" +#include "flutter/shell/platform/windows/external_texture.h" + +namespace flutter { + +// An external texture that is backed by a DXGI surface. +class ExternalTextureD3d : public ExternalTexture { + public: + ExternalTextureD3d( + FlutterDesktopGpuSurfaceType type, + const FlutterDesktopGpuSurfaceTextureCallback texture_callback, + void* user_data, + const AngleSurfaceManager* surface_manager, + const GlProcs& gl_procs); + virtual ~ExternalTextureD3d(); + + // |ExternalTexture| + bool PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) override; + + private: + // Creates or updates the backing texture and associates it with the provided + // surface. + bool CreateOrUpdateTexture( + const FlutterDesktopGpuSurfaceDescriptor* descriptor); + // Detaches the previously attached surface, if any. + void ReleaseImage(); + + FlutterDesktopGpuSurfaceType type_; + const FlutterDesktopGpuSurfaceTextureCallback texture_callback_; + void* const user_data_; + const AngleSurfaceManager* surface_manager_; + const GlProcs& gl_; + GLuint gl_texture_ = 0; + EGLSurface egl_surface_ = EGL_NO_SURFACE; + void* last_surface_handle_ = nullptr; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_D3D_H_ diff --git a/shell/platform/windows/external_texture_gl.h b/shell/platform/windows/external_texture_gl.h deleted file mode 100644 index 56d9021c855f6..0000000000000 --- a/shell/platform/windows/external_texture_gl.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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. - -#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_ -#define FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_ - -#include - -#include - -#include "flutter/shell/platform/common/public/flutter_texture_registrar.h" -#include "flutter/shell/platform/embedder/embedder.h" - -#include -#include - -namespace flutter { - -typedef struct ExternalTextureGLState ExternalTextureGLState; - -typedef void (*glGenTexturesProc)(GLsizei n, GLuint* textures); -typedef void (*glDeleteTexturesProc)(GLsizei n, const GLuint* textures); -typedef void (*glBindTextureProc)(GLenum target, GLuint texture); -typedef void (*glTexParameteriProc)(GLenum target, GLenum pname, GLint param); -typedef void (*glTexImage2DProc)(GLenum target, - GLint level, - GLint internalformat, - GLsizei width, - GLsizei height, - GLint border, - GLenum format, - GLenum type, - const void* data); - -// A struct containing pointers to resolved gl* functions. -struct GlProcs { - glGenTexturesProc glGenTextures; - glDeleteTexturesProc glDeleteTextures; - glBindTextureProc glBindTexture; - glTexParameteriProc glTexParameteri; - glTexImage2DProc glTexImage2D; - bool valid; -}; - -// An abstraction of an OpenGL texture. -class ExternalTextureGL { - public: - ExternalTextureGL(FlutterDesktopPixelBufferTextureCallback texture_callback, - void* user_data, - const GlProcs& gl_procs); - - virtual ~ExternalTextureGL(); - - // Returns the unique id of this texture. - int64_t texture_id() { return reinterpret_cast(this); } - - void MarkFrameAvailable(); - - // Attempts to populate the specified |opengl_texture| with texture details - // such as the name, width, height and the pixel format upon successfully - // copying the buffer provided by |texture_callback_|. See |CopyPixelBuffer|. - // Returns true on success or false if the pixel buffer could not be copied. - bool PopulateTexture(size_t width, - size_t height, - FlutterOpenGLTexture* opengl_texture); - - private: - // Attempts to copy the pixel buffer returned by |texture_callback_| to - // OpenGL. - // The |width| and |height| will be set to the actual bounds of the copied - // pixel buffer. - // Returns true on success or false if the pixel buffer returned - // by |texture_callback_| was invalid. - bool CopyPixelBuffer(size_t& width, size_t& height); - - std::unique_ptr state_; - FlutterDesktopPixelBufferTextureCallback texture_callback_ = nullptr; - void* user_data_ = nullptr; - const GlProcs& gl_; -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/windows/external_texture_gl.cc b/shell/platform/windows/external_texture_pixelbuffer.cc similarity index 78% rename from shell/platform/windows/external_texture_gl.cc rename to shell/platform/windows/external_texture_pixelbuffer.cc index b4dc9b1585bd8..2ead76a0ac430 100644 --- a/shell/platform/windows/external_texture_gl.cc +++ b/shell/platform/windows/external_texture_pixelbuffer.cc @@ -2,33 +2,33 @@ // 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/external_texture_gl.h" +#include "flutter/shell/platform/windows/external_texture_pixelbuffer.h" namespace flutter { -struct ExternalTextureGLState { +struct ExternalTexturePixelBufferState { GLuint gl_texture = 0; }; -ExternalTextureGL::ExternalTextureGL( +ExternalTexturePixelBuffer::ExternalTexturePixelBuffer( FlutterDesktopPixelBufferTextureCallback texture_callback, void* user_data, const GlProcs& gl_procs) - : state_(std::make_unique()), + : state_(std::make_unique()), texture_callback_(texture_callback), user_data_(user_data), gl_(gl_procs) {} -ExternalTextureGL::~ExternalTextureGL() { - const auto& gl = GlProcs(); +ExternalTexturePixelBuffer::~ExternalTexturePixelBuffer() { if (state_->gl_texture != 0) { gl_.glDeleteTextures(1, &state_->gl_texture); } } -bool ExternalTextureGL::PopulateTexture(size_t width, - size_t height, - FlutterOpenGLTexture* opengl_texture) { +bool ExternalTexturePixelBuffer::PopulateTexture( + size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) { if (!CopyPixelBuffer(width, height)) { return false; } @@ -45,7 +45,8 @@ bool ExternalTextureGL::PopulateTexture(size_t width, return true; } -bool ExternalTextureGL::CopyPixelBuffer(size_t& width, size_t& height) { +bool ExternalTexturePixelBuffer::CopyPixelBuffer(size_t& width, + size_t& height) { const FlutterDesktopPixelBuffer* pixel_buffer = texture_callback_(width, height, user_data_); if (!pixel_buffer || !pixel_buffer->buffer) { diff --git a/shell/platform/windows/external_texture_pixelbuffer.h b/shell/platform/windows/external_texture_pixelbuffer.h new file mode 100644 index 0000000000000..527f06b32a254 --- /dev/null +++ b/shell/platform/windows/external_texture_pixelbuffer.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_PIXELBUFFER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_PIXELBUFFER_H_ + +#include + +#include + +#include "flutter/shell/platform/common/public/flutter_texture_registrar.h" + +#include "flutter/shell/platform/windows/external_texture.h" + +namespace flutter { + +typedef struct ExternalTexturePixelBufferState ExternalTexturePixelBufferState; + +// An abstraction of an pixel-buffer based texture. +class ExternalTexturePixelBuffer : public ExternalTexture { + public: + ExternalTexturePixelBuffer( + FlutterDesktopPixelBufferTextureCallback texture_callback, + void* user_data, + const GlProcs& gl_procs); + + virtual ~ExternalTexturePixelBuffer(); + + // |ExternalTexture| + bool PopulateTexture(size_t width, + size_t height, + FlutterOpenGLTexture* opengl_texture) override; + + private: + // Attempts to copy the pixel buffer returned by |texture_callback_| to + // OpenGL. + // The |width| and |height| will be set to the actual bounds of the copied + // pixel buffer. + // Returns true on success or false if the pixel buffer returned + // by |texture_callback_| was invalid. + bool CopyPixelBuffer(size_t& width, size_t& height); + + std::unique_ptr state_; + FlutterDesktopPixelBufferTextureCallback texture_callback_ = nullptr; + void* const user_data_ = nullptr; + const GlProcs& gl_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_PIXELBUFFER_H_ diff --git a/shell/platform/windows/flutter_windows_texture_registrar.cc b/shell/platform/windows/flutter_windows_texture_registrar.cc index c698bd29fdf9d..9e3b3bd0f77a5 100644 --- a/shell/platform/windows/flutter_windows_texture_registrar.cc +++ b/shell/platform/windows/flutter_windows_texture_registrar.cc @@ -4,11 +4,18 @@ #include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" -#include "flutter/shell/platform/windows/flutter_windows_engine.h" - #include #include +#include "flutter/shell/platform/embedder/embedder_struct_macros.h" +#include "flutter/shell/platform/windows/external_texture_d3d.h" +#include "flutter/shell/platform/windows/external_texture_pixelbuffer.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" + +namespace { +static constexpr int64_t kInvalidTexture = -1; +} + namespace flutter { FlutterWindowsTextureRegistrar::FlutterWindowsTextureRegistrar( @@ -19,28 +26,48 @@ FlutterWindowsTextureRegistrar::FlutterWindowsTextureRegistrar( int64_t FlutterWindowsTextureRegistrar::RegisterTexture( const FlutterDesktopTextureInfo* texture_info) { if (!gl_procs_.valid) { - return -1; + return kInvalidTexture; } - if (texture_info->type != kFlutterDesktopPixelBufferTexture) { - std::cerr << "Attempted to register texture of unsupport type." - << std::endl; - return -1; - } + if (texture_info->type == kFlutterDesktopPixelBufferTexture) { + if (!texture_info->pixel_buffer_config.callback) { + std::cerr << "Invalid pixel buffer texture callback." << std::endl; + return kInvalidTexture; + } - if (!texture_info->pixel_buffer_config.callback) { - std::cerr << "Invalid pixel buffer texture callback." << std::endl; - return -1; + return EmplaceTexture(std::make_unique( + texture_info->pixel_buffer_config.callback, + texture_info->pixel_buffer_config.user_data, gl_procs_)); + } else if (texture_info->type == kFlutterDesktopGpuSurfaceTexture) { + const FlutterDesktopGpuSurfaceTextureConfig* gpu_surface_config = + &texture_info->gpu_surface_config; + auto surface_type = SAFE_ACCESS(gpu_surface_config, type, + kFlutterDesktopGpuSurfaceTypeNone); + if (surface_type == kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle || + surface_type == kFlutterDesktopGpuSurfaceTypeD3d11Texture2D) { + auto callback = SAFE_ACCESS(gpu_surface_config, callback, nullptr); + if (!callback) { + std::cerr << "Invalid GPU surface descriptor callback." << std::endl; + return kInvalidTexture; + } + + auto user_data = SAFE_ACCESS(gpu_surface_config, user_data, nullptr); + return EmplaceTexture(std::make_unique( + surface_type, callback, user_data, engine_->surface_manager(), + gl_procs_)); + } } - auto texture_gl = std::make_unique( - texture_info->pixel_buffer_config.callback, - texture_info->pixel_buffer_config.user_data, gl_procs_); - int64_t texture_id = texture_gl->texture_id(); + std::cerr << "Attempted to register texture of unsupport type." << std::endl; + return kInvalidTexture; +} +int64_t FlutterWindowsTextureRegistrar::EmplaceTexture( + std::unique_ptr texture) { + int64_t texture_id = texture->texture_id(); { std::lock_guard lock(map_mutex_); - textures_[texture_id] = std::move(texture_gl); + textures_[texture_id] = std::move(texture); } engine_->task_runner()->RunNowOrPostTask([engine = engine_, texture_id]() { @@ -79,7 +106,7 @@ bool FlutterWindowsTextureRegistrar::PopulateTexture( size_t width, size_t height, FlutterOpenGLTexture* opengl_texture) { - flutter::ExternalTextureGL* texture; + flutter::ExternalTexture* texture; { std::lock_guard lock(map_mutex_); auto it = textures_.find(texture_id); diff --git a/shell/platform/windows/flutter_windows_texture_registrar.h b/shell/platform/windows/flutter_windows_texture_registrar.h index a684136bfd105..f1d2ac0a3b25c 100644 --- a/shell/platform/windows/flutter_windows_texture_registrar.h +++ b/shell/platform/windows/flutter_windows_texture_registrar.h @@ -9,7 +9,8 @@ #include #include -#include "flutter/shell/platform/windows/external_texture_gl.h" +#include "flutter/shell/platform/common/public/flutter_texture_registrar.h" +#include "flutter/shell/platform/windows/external_texture.h" namespace flutter { @@ -50,9 +51,11 @@ class FlutterWindowsTextureRegistrar { const GlProcs& gl_procs_; // All registered textures, keyed by their IDs. - std::unordered_map> + std::unordered_map> textures_; std::mutex map_mutex_; + + int64_t EmplaceTexture(std::unique_ptr texture); }; }; // namespace flutter diff --git a/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc b/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc index dc29dbebab9f5..f0bddc1ae9f3f 100644 --- a/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc +++ b/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc @@ -14,6 +14,8 @@ namespace flutter { namespace testing { +using Microsoft::WRL::ComPtr; + namespace { // Returns an engine instance configured with dummy project path values. std::unique_ptr GetTestEngine() { @@ -22,14 +24,46 @@ std::unique_ptr GetTestEngine() { properties.icu_data_path = L"C:\\foo\\icudtl.dat"; properties.aot_library_path = L"C:\\foo\\aot.so"; FlutterProjectBundle project(properties); - return std::make_unique(project); + auto engine = std::make_unique(project); + + EngineModifier modifier(engine.get()); + modifier.embedder_api().RegisterExternalTexture = + MOCK_ENGINE_PROC(RegisterExternalTexture, + ([](auto engine, auto texture_id) { return kSuccess; })); + + return engine; +} + +// Creates a ID3D11Texture2D with the specified size. +ComPtr CreateD3dTexture(FlutterWindowsEngine* engine, + UINT width, + UINT height) { + ComPtr d3d_device; + ComPtr d3d_texture; + if (engine->surface_manager()->GetDevice(d3d_device.GetAddressOf())) { + D3D11_TEXTURE2D_DESC texture_description = {}; + texture_description.MipLevels = 1; + texture_description.SampleDesc.Count = 1; + texture_description.BindFlags = D3D11_BIND_RENDER_TARGET; + texture_description.Usage = D3D11_USAGE_DEFAULT; + texture_description.CPUAccessFlags = 0; + texture_description.ArraySize = 1; + texture_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + texture_description.Height = width; + texture_description.Width = height; + texture_description.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + + d3d_device->CreateTexture2D(&texture_description, nullptr, + d3d_texture.GetAddressOf()); + } + return d3d_texture; } + } // namespace TEST(FlutterWindowsTextureRegistrarTest, CreateDestroy) { std::unique_ptr engine = GetTestEngine(); std::unique_ptr gl = std::make_unique(); - FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs()); EXPECT_TRUE(true); @@ -107,9 +141,7 @@ TEST(FlutterWindowsTextureRegistrarTest, RegisterUnknownTextureType) { TEST(FlutterWindowsTextureRegistrarTest, PopulatePixelBufferTexture) { std::unique_ptr engine = GetTestEngine(); - EngineModifier modifier(engine.get()); std::unique_ptr gl = std::make_unique(); - FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs()); bool release_callback_called = false; @@ -136,9 +168,109 @@ TEST(FlutterWindowsTextureRegistrarTest, PopulatePixelBufferTexture) { return reinterpret_cast(user_data); }; - modifier.embedder_api().RegisterExternalTexture = - MOCK_ENGINE_PROC(RegisterExternalTexture, - ([](auto engine, auto texture_id) { return kSuccess; })); + FlutterOpenGLTexture flutter_texture = {}; + auto texture_id = registrar.RegisterTexture(&texture_info); + EXPECT_NE(texture_id, -1); + + auto result = + registrar.PopulateTexture(texture_id, 640, 480, &flutter_texture); + EXPECT_TRUE(result); + EXPECT_EQ(flutter_texture.width, width); + EXPECT_EQ(flutter_texture.height, height); + EXPECT_EQ(flutter_texture.target, GL_TEXTURE_2D); + EXPECT_TRUE(release_callback_called); +} + +TEST(FlutterWindowsTextureRegistrarTest, PopulateD3dTextureWithHandle) { + std::unique_ptr engine = GetTestEngine(); + std::unique_ptr gl = std::make_unique(); + FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs()); + + UINT width = 100; + UINT height = 100; + auto d3d_texture = CreateD3dTexture(engine.get(), width, height); + EXPECT_TRUE(d3d_texture); + + ComPtr shared_resource; + EXPECT_TRUE(SUCCEEDED(d3d_texture.As(&shared_resource))); + + HANDLE shared_handle; + EXPECT_TRUE(SUCCEEDED(shared_resource->GetSharedHandle(&shared_handle))); + + bool release_callback_called = false; + FlutterDesktopGpuSurfaceDescriptor surface_descriptor = {}; + surface_descriptor.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor); + surface_descriptor.handle = shared_handle; + surface_descriptor.width = surface_descriptor.visible_width = width; + surface_descriptor.height = surface_descriptor.visible_height = height; + surface_descriptor.release_context = &release_callback_called; + surface_descriptor.release_callback = [](void* release_context) { + bool* called = reinterpret_cast(release_context); + *called = true; + }; + + FlutterDesktopTextureInfo texture_info = {}; + texture_info.type = kFlutterDesktopGpuSurfaceTexture; + texture_info.gpu_surface_config.struct_size = + sizeof(FlutterDesktopGpuSurfaceTextureConfig); + texture_info.gpu_surface_config.type = + kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle; + texture_info.gpu_surface_config.user_data = &surface_descriptor; + texture_info.gpu_surface_config.callback = + [](size_t width, size_t height, + void* user_data) -> const FlutterDesktopGpuSurfaceDescriptor* { + return reinterpret_cast( + user_data); + }; + + FlutterOpenGLTexture flutter_texture = {}; + auto texture_id = registrar.RegisterTexture(&texture_info); + EXPECT_NE(texture_id, -1); + + auto result = + registrar.PopulateTexture(texture_id, 640, 480, &flutter_texture); + EXPECT_TRUE(result); + EXPECT_EQ(flutter_texture.width, width); + EXPECT_EQ(flutter_texture.height, height); + EXPECT_EQ(flutter_texture.target, GL_TEXTURE_2D); + EXPECT_TRUE(release_callback_called); +} + +TEST(FlutterWindowsTextureRegistrarTest, PopulateD3dTexture) { + std::unique_ptr engine = GetTestEngine(); + std::unique_ptr gl = std::make_unique(); + FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs()); + + UINT width = 100; + UINT height = 100; + auto d3d_texture = CreateD3dTexture(engine.get(), width, height); + EXPECT_TRUE(d3d_texture); + + bool release_callback_called = false; + FlutterDesktopGpuSurfaceDescriptor surface_descriptor = {}; + surface_descriptor.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor); + surface_descriptor.handle = d3d_texture.Get(); + surface_descriptor.width = surface_descriptor.visible_width = width; + surface_descriptor.height = surface_descriptor.visible_height = height; + surface_descriptor.release_context = &release_callback_called; + surface_descriptor.release_callback = [](void* release_context) { + bool* called = reinterpret_cast(release_context); + *called = true; + }; + + FlutterDesktopTextureInfo texture_info = {}; + texture_info.type = kFlutterDesktopGpuSurfaceTexture; + texture_info.gpu_surface_config.struct_size = + sizeof(FlutterDesktopGpuSurfaceTextureConfig); + texture_info.gpu_surface_config.type = + kFlutterDesktopGpuSurfaceTypeD3d11Texture2D; + texture_info.gpu_surface_config.user_data = &surface_descriptor; + texture_info.gpu_surface_config.callback = + [](size_t width, size_t height, + void* user_data) -> const FlutterDesktopGpuSurfaceDescriptor* { + return reinterpret_cast( + user_data); + }; FlutterOpenGLTexture flutter_texture = {}; auto texture_id = registrar.RegisterTexture(&texture_info); diff --git a/shell/platform/windows/testing/mock_gl_functions.h b/shell/platform/windows/testing/mock_gl_functions.h index ec0871b886dae..0d46cbb368d75 100644 --- a/shell/platform/windows/testing/mock_gl_functions.h +++ b/shell/platform/windows/testing/mock_gl_functions.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_GL_FUNCTIONS_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_GL_FUNCTIONS_H_ -#include "flutter/shell/platform/windows/external_texture_gl.h" +#include "flutter/shell/platform/windows/external_texture.h" namespace flutter { namespace testing {