Skip to content

Commit

Permalink
[embedder] [metal] Add support for Metal Renderer Config in the embed…
Browse files Browse the repository at this point in the history
…der API (flutter#22854)

This change adds a FlutterMetalRendererConfig that lets embedders
specify metal as rendering api.

Also adds a test that validates rendering a gradient using metal.
  • Loading branch information
iskakaushik authored Jan 2, 2021
1 parent f9f4d01 commit 70f0702
Show file tree
Hide file tree
Showing 38 changed files with 1,042 additions and 197 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,8 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.h
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.mm
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h
FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc
Expand All @@ -1913,6 +1915,7 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_root_lay
FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_noxform.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_xform.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_metal.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_xform.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart
FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor.png
Expand Down
4 changes: 2 additions & 2 deletions shell/gpu/gpu_surface_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
return nullptr;
}

auto submit_callback = [texture_id = texture.texture_id, delegate = delegate_](
auto submit_callback = [texture = texture, delegate = delegate_](
const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
TRACE_EVENT0("flutter", "GPUSurfaceMetal::PresentTexture");
if (canvas == nullptr) {
Expand All @@ -135,7 +135,7 @@

canvas->flush();

return delegate->PresentTexture(texture_id);
return delegate->PresentTexture(texture);
};

return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
Expand Down
6 changes: 3 additions & 3 deletions shell/gpu/gpu_surface_metal_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ typedef void* GPUMTLCommandQueueHandle;
typedef void* GPUCAMetalLayerHandle;

// expected to be id<MTLTexture>
typedef void* GPUMTLTextureHandle;
typedef const void* GPUMTLTextureHandle;

struct GPUMTLTextureInfo {
intptr_t texture_id;
int64_t texture_id;
GPUMTLTextureHandle texture;
};

Expand Down Expand Up @@ -87,7 +87,7 @@ class GPUSurfaceMetalDelegate {
///
/// @see |GPUSurfaceMetalDelegate::GetMTLTexture|
///
virtual bool PresentTexture(intptr_t texture_id) const = 0;
virtual bool PresentTexture(GPUMTLTextureInfo texture) const = 0;

MTLRenderTargetType GetRenderTargetType();

Expand Down
2 changes: 1 addition & 1 deletion shell/platform/darwin/ios/ios_surface_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetal final : public IOSSurface,
GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override;

// |GPUSurfaceMetalDelegate|
bool PresentTexture(intptr_t texture_id) const override;
bool PresentTexture(GPUMTLTextureInfo texture) const override;

FML_DISALLOW_COPY_AND_ASSIGN(IOSSurfaceMetal);
};
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/darwin/ios/ios_surface_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
}

// |GPUSurfaceMetalDelegate|
bool IOSSurfaceMetal::PresentTexture(intptr_t texture_id) const {
bool IOSSurfaceMetal::PresentTexture(GPUMTLTextureInfo texture) const {
FML_CHECK(false) << "render to texture not supported on ios";
return false;
}
Expand Down
23 changes: 23 additions & 0 deletions shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ template("embedder_source_set") {
"//third_party/skia",
]

if (embedder_enable_metal) {
sources += [
"embedder_surface_metal.h",
"embedder_surface_metal.mm",
]

cflags_objc = flutter_cflags_objc
cflags_objcc = flutter_cflags_objcc

deps += [ "//flutter/shell/platform/darwin/graphics" ]
}

public_deps = [ ":embedder_headers" ]

public_configs += [
Expand Down Expand Up @@ -143,6 +155,7 @@ test_fixtures("fixtures") {
"fixtures/dpr_noxform.png",
"fixtures/dpr_xform.png",
"fixtures/gradient.png",
"fixtures/gradient_metal.png",
"fixtures/gradient_xform.png",
"fixtures/scene_without_custom_compositor.png",
"fixtures/scene_without_custom_compositor_with_xform.png",
Expand Down Expand Up @@ -208,6 +221,16 @@ if (enable_unittests) {

deps += [ "//flutter/testing:opengl" ]
}

if (test_enable_metal) {
sources += [
"tests/embedder_test_context_metal.cc",
"tests/embedder_test_context_metal.h",
"tests/embedder_unittests_metal.cc",
]

deps += [ "//flutter/testing:metal" ]
}
}

# Tests the build in FLUTTER_ENGINE_NO_PROTOTYPES mode.
Expand Down
96 changes: 96 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ extern const intptr_t kPlatformStrongDillSize;
#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h"
#endif

#ifdef SHELL_ENABLE_METAL
#include "flutter/shell/platform/embedder/embedder_surface_metal.h"
#endif

const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;

Expand Down Expand Up @@ -121,6 +125,24 @@ static bool IsSoftwareRendererConfigValid(const FlutterRendererConfig* config) {
return true;
}

static bool IsMetalRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kMetal) {
return false;
}

const FlutterMetalRendererConfig* metal_config = &config->metal;

bool device = SAFE_ACCESS(metal_config, device, nullptr);
bool command_queue =
SAFE_ACCESS(metal_config, present_command_queue, nullptr);

bool present = SAFE_ACCESS(metal_config, present_drawable_callback, nullptr);
bool get_texture =
SAFE_ACCESS(metal_config, get_next_drawable_callback, nullptr);

return device && command_queue && present && get_texture;
}

static bool IsRendererValid(const FlutterRendererConfig* config) {
if (config == nullptr) {
return false;
Expand All @@ -131,6 +153,8 @@ static bool IsRendererValid(const FlutterRendererConfig* config) {
return IsOpenGLRendererConfigValid(config);
case kSoftware:
return IsSoftwareRendererConfigValid(config);
case kMetal:
return IsMetalRendererConfigValid(config);
default:
return false;
}
Expand Down Expand Up @@ -275,6 +299,74 @@ InferOpenGLPlatformViewCreationCallback(
#endif
}

static flutter::Shell::CreateCallback<flutter::PlatformView>
InferMetalPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
flutter::PlatformViewEmbedder::PlatformDispatchTable
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config->type != kMetal) {
return nullptr;
}

#ifdef SHELL_ENABLE_METAL
std::function<bool(flutter::GPUMTLTextureInfo texture)> metal_present =
[ptr = config->metal.present_drawable_callback,
user_data](flutter::GPUMTLTextureInfo texture) {
FlutterMetalTexture embedder_texture;
embedder_texture.struct_size = sizeof(FlutterMetalTexture);
embedder_texture.texture = texture.texture;
embedder_texture.texture_id = texture.texture_id;
return ptr(user_data, &embedder_texture);
};
auto metal_get_texture =
[ptr = config->metal.get_next_drawable_callback,
user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo {
FlutterFrameInfo frame_info = {};
frame_info.struct_size = sizeof(FlutterFrameInfo);
frame_info.size = {static_cast<uint32_t>(frame_size.width()),
static_cast<uint32_t>(frame_size.height())};
flutter::GPUMTLTextureInfo texture_info;

FlutterMetalTexture metal_texture = ptr(user_data, &frame_info);
texture_info.texture_id = metal_texture.texture_id;
texture_info.texture = metal_texture.texture;
return texture_info;
};

flutter::EmbedderSurfaceMetal::MetalDispatchTable metal_dispatch_table = {
.present = metal_present,
.get_texture = metal_get_texture,
};

std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);

std::unique_ptr<flutter::EmbedderSurfaceMetal> embedder_surface =
std::make_unique<flutter::EmbedderSurfaceMetal>(
const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device),
const_cast<flutter::GPUMTLCommandQueueHandle>(
config->metal.present_command_queue),
metal_dispatch_table, view_embedder);

return fml::MakeCopyable(
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
external_view_embedder = view_embedder](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::move(embedder_surface), // embedder surface
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}

static flutter::Shell::CreateCallback<flutter::PlatformView>
InferSoftwarePlatformViewCreationCallback(
const FlutterRendererConfig* config,
Expand Down Expand Up @@ -333,6 +425,10 @@ InferPlatformViewCreationCallback(
return InferSoftwarePlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
case kMetal:
return InferMetalPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
default:
return nullptr;
}
Expand Down
54 changes: 54 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ typedef enum {
typedef enum {
kOpenGL,
kSoftware,
/// Metal is only supported on Darwin platforms (macOS / iOS).
/// iOS version >= 10.0 (device), 13.0 (simulator)
/// macOS version >= 10.14
kMetal,
} FlutterRendererType;

/// Additional accessibility features that may be enabled by the platform.
Expand Down Expand Up @@ -434,6 +438,55 @@ typedef struct {
BoolPresentInfoCallback present_with_info;
} FlutterOpenGLRendererConfig;

/// Alias for id<MTLDevice>.
typedef const void* FlutterMetalDeviceHandle;

/// Alias for id<MTLCommandQueue>.
typedef const void* FlutterMetalCommandQueueHandle;

/// Alias for id<MTLTexture>.
typedef const void* FlutterMetalTextureHandle;

typedef struct {
/// The size of this struct. Must be sizeof(FlutterMetalTexture).
size_t struct_size;
/// Embedder provided unique identifier to the texture buffer. Given that the
/// `texture` handle is passed to the engine to render to, the texture buffer
/// is itseld owned by the embedder. This `texture_id` is then also given to
/// the embedder in the present callback.
int64_t texture_id;
/// Handle to the MTLTexture that is owned by the embedder. Engine will render
/// the frame into this texture.
FlutterMetalTextureHandle texture;
} FlutterMetalTexture;

/// Callback for when a metal texture is requested.
typedef FlutterMetalTexture (*FlutterMetalTextureCallback)(
void* /* user data */,
const FlutterFrameInfo* /* frame info */);

/// Callback for when a metal texture is presented. The texture_id here
/// corresponds to the texture_id provided by the embedder in the
/// `FlutterMetalTextureCallback` callback.
typedef bool (*FlutterMetalPresentCallback)(
void* /* user data */,
const FlutterMetalTexture* /* texture */);

typedef struct {
/// The size of this struct. Must be sizeof(FlutterMetalRendererConfig).
size_t struct_size;
/// Alias for id<MTLDevice>.
FlutterMetalDeviceHandle device;
/// Alias for id<MTLCommandQueue>.
FlutterMetalCommandQueueHandle present_command_queue;
/// The callback that gets invoked when the engine requests the embedder for a
/// texture to render to.
FlutterMetalTextureCallback get_next_drawable_callback;
/// The callback presented to the embedder to present a fully populated metal
/// texture to the user.
FlutterMetalPresentCallback present_drawable_callback;
} FlutterMetalRendererConfig;

typedef struct {
/// The size of this struct. Must be sizeof(FlutterSoftwareRendererConfig).
size_t struct_size;
Expand All @@ -449,6 +502,7 @@ typedef struct {
union {
FlutterOpenGLRendererConfig open_gl;
FlutterSoftwareRendererConfig software;
FlutterMetalRendererConfig metal;
};
} FlutterRendererConfig;

Expand Down
68 changes: 68 additions & 0 deletions shell/platform/embedder/embedder_surface_metal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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_EMBEDDER_EMBEDDER_SURFACE_METAL_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SURFACE_METAL_H_

#include "flutter/fml/macros.h"
#include "flutter/shell/gpu/gpu_surface_metal.h"
#include "flutter/shell/gpu/gpu_surface_metal_delegate.h"
#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h"
#include "flutter/shell/platform/embedder/embedder_surface.h"

namespace flutter {

class EmbedderSurfaceMetal final : public EmbedderSurface,
public GPUSurfaceMetalDelegate {
public:
struct MetalDispatchTable {
std::function<bool(GPUMTLTextureInfo texture)> present; // required
std::function<GPUMTLTextureInfo(const SkISize& frame_size)>
get_texture; // required
};

EmbedderSurfaceMetal(
GPUMTLDeviceHandle device,
GPUMTLCommandQueueHandle command_queue,
MetalDispatchTable dispatch_table,
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder);

~EmbedderSurfaceMetal() override;

private:
bool valid_ = false;
MetalDispatchTable metal_dispatch_table_;
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder_;
sk_sp<SkSurface> surface_;
sk_sp<GrDirectContext> main_context_;
sk_sp<GrDirectContext> resource_context_;

// |EmbedderSurface|
bool IsValid() const override;

// |EmbedderSurface|
std::unique_ptr<Surface> CreateGPUSurface() override;

// |EmbedderSurface|
sk_sp<GrDirectContext> CreateResourceContext() const override;

// |GPUSurfaceMetalDelegate|
GPUCAMetalLayerHandle GetCAMetalLayer(
const SkISize& frame_size) const override;

// |GPUSurfaceMetalDelegate|
bool PresentDrawable(GrMTLHandle drawable) const override;

// |GPUSurfaceMetalDelegate|
GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_size) const override;

// |GPUSurfaceMetalDelegate|
bool PresentTexture(GPUMTLTextureInfo texture) const override;

FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceMetal);
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SURFACE_METAL_H_
Loading

0 comments on commit 70f0702

Please sign in to comment.