Windows: Add Direct3D texture interoperability support (flutter/engine#26840)

This commit is contained in:
Niklas Schulze 2022-04-30 14:13:39 +02:00 committed by GitHub
parent 6c23dd0aab
commit fa609bd3fa
17 changed files with 699 additions and 143 deletions

View File

@ -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

View File

@ -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<PixelBufferTexture>(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<PixelBufferTexture*>(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<GpuSurfaceTexture>(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<GpuSurfaceTexture*>(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) {

View File

@ -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<PixelBufferTexture> TextureVariant;
typedef std::variant<PixelBufferTexture, GpuSurfaceTexture> TextureVariant;
// An object keeping track of external textures.
//

View File

@ -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;

View File

@ -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",

View File

@ -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<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
eglGetProcAddress("eglQueryDisplayAttribEXT"));
PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT =
reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
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<EGLDeviceEXT>(egl_device),
EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) {
resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
}
}
}
resolved_device_.CopyTo(device);
return (resolved_device_ != nullptr);
}
} // namespace flutter

View File

@ -13,7 +13,9 @@
#include <GLES2/gl2ext.h>
// Windows platform specific includes
#include <d3d11.h>
#include <windows.h>
#include <wrl/client.h>
#include <memory>
#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<ID3D11Device> resolved_device_;
// Number of active instances of AngleSurfaceManager
static int instance_count_;
};

View File

@ -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 <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
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<int64_t>(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_

View File

@ -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 <EGL/egl.h>
#include <EGL/eglext.h>
#include <iostream>
#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<EGLint>(SAFE_ACCESS(descriptor, width, 0)),
EGL_HEIGHT,
static_cast<EGLint>(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

View File

@ -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 <stdint.h>
#include <memory>
#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_

View File

@ -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 <stdint.h>
#include <memory>
#include "flutter/shell/platform/common/public/flutter_texture_registrar.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
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<int64_t>(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<ExternalTextureGLState> state_;
FlutterDesktopPixelBufferTextureCallback texture_callback_ = nullptr;
void* user_data_ = nullptr;
const GlProcs& gl_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_GL_H_

View File

@ -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<ExternalTextureGLState>()),
: state_(std::make_unique<ExternalTexturePixelBufferState>()),
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) {

View File

@ -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 <stdint.h>
#include <memory>
#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<ExternalTexturePixelBufferState> state_;
FlutterDesktopPixelBufferTextureCallback texture_callback_ = nullptr;
void* const user_data_ = nullptr;
const GlProcs& gl_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_EXTERNAL_TEXTURE_PIXELBUFFER_H_

View File

@ -4,11 +4,18 @@
#include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include <iostream>
#include <mutex>
#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;
}
return EmplaceTexture(std::make_unique<flutter::ExternalTexturePixelBuffer>(
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<flutter::ExternalTextureD3d>(
surface_type, callback, user_data, engine_->surface_manager(),
gl_procs_));
}
}
if (!texture_info->pixel_buffer_config.callback) {
std::cerr << "Invalid pixel buffer texture callback." << std::endl;
return -1;
}
auto texture_gl = std::make_unique<flutter::ExternalTextureGL>(
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<ExternalTexture> texture) {
int64_t texture_id = texture->texture_id();
{
std::lock_guard<std::mutex> 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<std::mutex> lock(map_mutex_);
auto it = textures_.find(texture_id);

View File

@ -9,7 +9,8 @@
#include <mutex>
#include <unordered_map>
#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<int64_t, std::unique_ptr<flutter::ExternalTextureGL>>
std::unordered_map<int64_t, std::unique_ptr<flutter::ExternalTexture>>
textures_;
std::mutex map_mutex_;
int64_t EmplaceTexture(std::unique_ptr<ExternalTexture> texture);
};
}; // namespace flutter

View File

@ -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<FlutterWindowsEngine> GetTestEngine() {
@ -22,14 +24,46 @@ std::unique_ptr<FlutterWindowsEngine> 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<FlutterWindowsEngine>(project);
auto engine = std::make_unique<FlutterWindowsEngine>(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<ID3D11Texture2D> CreateD3dTexture(FlutterWindowsEngine* engine,
UINT width,
UINT height) {
ComPtr<ID3D11Device> d3d_device;
ComPtr<ID3D11Texture2D> 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<FlutterWindowsEngine> engine = GetTestEngine();
std::unique_ptr<MockGlFunctions> gl = std::make_unique<MockGlFunctions>();
FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs());
EXPECT_TRUE(true);
@ -107,9 +141,7 @@ TEST(FlutterWindowsTextureRegistrarTest, RegisterUnknownTextureType) {
TEST(FlutterWindowsTextureRegistrarTest, PopulatePixelBufferTexture) {
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
EngineModifier modifier(engine.get());
std::unique_ptr<MockGlFunctions> gl = std::make_unique<MockGlFunctions>();
FlutterWindowsTextureRegistrar registrar(engine.get(), gl->gl_procs());
bool release_callback_called = false;
@ -136,9 +168,109 @@ TEST(FlutterWindowsTextureRegistrarTest, PopulatePixelBufferTexture) {
return reinterpret_cast<const FlutterDesktopPixelBuffer*>(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<FlutterWindowsEngine> engine = GetTestEngine();
std::unique_ptr<MockGlFunctions> gl = std::make_unique<MockGlFunctions>();
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<IDXGIResource> 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<bool*>(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<const FlutterDesktopGpuSurfaceDescriptor*>(
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<FlutterWindowsEngine> engine = GetTestEngine();
std::unique_ptr<MockGlFunctions> gl = std::make_unique<MockGlFunctions>();
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<bool*>(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<const FlutterDesktopGpuSurfaceDescriptor*>(
user_data);
};
FlutterOpenGLTexture flutter_texture = {};
auto texture_id = registrar.RegisterTexture(&texture_info);

View File

@ -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 {