[Impeller] libImpeller: Add support for Metal and Vulkan rendering. (#161547)

* Adds context creation and WSI routines for Metal and Vulkan.
* Enables all tests for the Metal, Vulkan, and OpenGLES backends.
* Separate standalone examples for Metal, Vulkan, and OpenGLES have been
created. These will be packaged with the SDK.
  * Disallows the use of OpenGL ES on macOS.
* All new public methods are documented.
* The SDK version number has been bumped.
* Some incorrect nullability annotations were patched.
* Tests harness is overhauled to reuse the same underlying context as
the playgrounds.
* The C++ public header has been updated.

Fixes https://github.com/flutter/flutter/issues/159512
Ports https://github.com/flutter/engine/pull/56906
This commit is contained in:
Chinmay Garde 2025-02-11 16:47:19 -08:00 committed by GitHub
parent c5a144435a
commit 8c843aa9d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 1768 additions and 182 deletions

View File

@ -42277,6 +42277,22 @@ ORIGIN: ../../../flutter/impeller/toolkit/glvk/proc_table.cc + ../../../flutter/
ORIGIN: ../../../flutter/impeller/toolkit/glvk/proc_table.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/glvk/trampoline.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/glvk/trampoline.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/context_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/context_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/reactor_worker_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/reactor_worker_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/surface_gles.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/gles/surface_gles.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/metal/context_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/metal/surface_mtl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/metal/surface_mtl.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/context_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/surface_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/surface_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/swapchain_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/backend/vulkan/swapchain_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/color_filter.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/color_filter.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/color_source.cc + ../../../flutter/LICENSE
@ -42287,7 +42303,9 @@ ORIGIN: ../../../flutter/impeller/toolkit/interop/dl.cc + ../../../flutter/LICEN
ORIGIN: ../../../flutter/impeller/toolkit/interop/dl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/dl_builder.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/dl_builder.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/example.c + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/example_gl.c + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/example_mtl.m + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/example_vk.c + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/formats.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/formats.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/toolkit/interop/image_filter.cc + ../../../flutter/LICENSE
@ -45229,6 +45247,22 @@ FILE: ../../../flutter/impeller/toolkit/glvk/proc_table.cc
FILE: ../../../flutter/impeller/toolkit/glvk/proc_table.h
FILE: ../../../flutter/impeller/toolkit/glvk/trampoline.cc
FILE: ../../../flutter/impeller/toolkit/glvk/trampoline.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/context_gles.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/context_gles.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/reactor_worker_gles.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/reactor_worker_gles.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/surface_gles.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/gles/surface_gles.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/metal/context_mtl.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/metal/context_mtl.mm
FILE: ../../../flutter/impeller/toolkit/interop/backend/metal/surface_mtl.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/metal/surface_mtl.mm
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/context_vk.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/surface_vk.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/surface_vk.h
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/swapchain_vk.cc
FILE: ../../../flutter/impeller/toolkit/interop/backend/vulkan/swapchain_vk.h
FILE: ../../../flutter/impeller/toolkit/interop/color_filter.cc
FILE: ../../../flutter/impeller/toolkit/interop/color_filter.h
FILE: ../../../flutter/impeller/toolkit/interop/color_source.cc
@ -45239,7 +45273,9 @@ FILE: ../../../flutter/impeller/toolkit/interop/dl.cc
FILE: ../../../flutter/impeller/toolkit/interop/dl.h
FILE: ../../../flutter/impeller/toolkit/interop/dl_builder.cc
FILE: ../../../flutter/impeller/toolkit/interop/dl_builder.h
FILE: ../../../flutter/impeller/toolkit/interop/example.c
FILE: ../../../flutter/impeller/toolkit/interop/example_gl.c
FILE: ../../../flutter/impeller/toolkit/interop/example_mtl.m
FILE: ../../../flutter/impeller/toolkit/interop/example_vk.c
FILE: ../../../flutter/impeller/toolkit/interop/formats.cc
FILE: ../../../flutter/impeller/toolkit/interop/formats.h
FILE: ../../../flutter/impeller/toolkit/interop/image_filter.cc

View File

@ -95,7 +95,6 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches)
context_settings.enable_validation = switches_.enable_vulkan_validation;
context_settings.fatal_missing_validations =
switches_.enable_vulkan_validation;
;
auto context_vk = ContextVK::Create(std::move(context_settings));
if (!context_vk || !context_vk->IsValid()) {
@ -237,4 +236,13 @@ bool PlaygroundImplVK::IsVulkanDriverPresent() {
return false;
}
// |PlaygroundImpl|
Playground::VKProcAddressResolver
PlaygroundImplVK::CreateVKProcAddressResolver() const {
return [](void* instance, const char* proc_name) -> void* {
return reinterpret_cast<void*>(::glfwGetInstanceProcAddress(
reinterpret_cast<VkInstance>(instance), proc_name));
};
}
} // namespace impeller

View File

@ -44,6 +44,10 @@ class PlaygroundImplVK final : public PlaygroundImpl {
std::unique_ptr<Surface> AcquireSurfaceFrame(
std::shared_ptr<Context> context) override;
// |PlaygroundImpl|
Playground::VKProcAddressResolver CreateVKProcAddressResolver()
const override;
PlaygroundImplVK(const PlaygroundImplVK&) = delete;
PlaygroundImplVK& operator=(const PlaygroundImplVK&) = delete;

View File

@ -521,4 +521,9 @@ Playground::GLProcAddressResolver Playground::CreateGLProcAddressResolver()
return impl_->CreateGLProcAddressResolver();
}
Playground::VKProcAddressResolver Playground::CreateVKProcAddressResolver()
const {
return impl_->CreateVKProcAddressResolver();
}
} // namespace impeller

View File

@ -118,6 +118,10 @@ class Playground {
using GLProcAddressResolver = std::function<void*(const char* proc_name)>;
GLProcAddressResolver CreateGLProcAddressResolver() const;
using VKProcAddressResolver =
std::function<void*(void* instance, const char* proc_name)>;
VKProcAddressResolver CreateVKProcAddressResolver() const;
protected:
const PlaygroundSwitches switches_;

View File

@ -71,4 +71,9 @@ Playground::GLProcAddressResolver PlaygroundImpl::CreateGLProcAddressResolver()
return nullptr;
}
Playground::VKProcAddressResolver PlaygroundImpl::CreateVKProcAddressResolver()
const {
return nullptr;
}
} // namespace impeller

View File

@ -38,6 +38,8 @@ class PlaygroundImpl {
virtual Playground::GLProcAddressResolver CreateGLProcAddressResolver() const;
virtual Playground::VKProcAddressResolver CreateVKProcAddressResolver() const;
protected:
const PlaygroundSwitches switches_;

View File

@ -144,6 +144,7 @@ void ContextVK::Setup(Settings settings) {
TRACE_EVENT0("impeller", "ContextVK::Setup");
if (!settings.proc_address_callback) {
VALIDATION_LOG << "Missing proc address callback.";
return;
}
@ -155,7 +156,7 @@ void ContextVK::Setup(Settings settings) {
fml::RequestAffinity(fml::CpuAffinity::kNotPerformance);
#ifdef FML_OS_ANDROID
if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) {
FML_LOG(ERROR) << "Failed to set Workers task runner priority";
VALIDATION_LOG << "Failed to set Workers task runner priority";
}
#endif // FML_OS_ANDROID
});

View File

@ -279,6 +279,38 @@ const ISize& KHRSwapchainImplVK::GetSize() const {
return size_;
}
std::optional<ISize> KHRSwapchainImplVK::GetCurrentUnderlyingSurfaceSize()
const {
if (!IsValid()) {
return std::nullopt;
}
auto context = context_.lock();
if (!context) {
return std::nullopt;
}
auto& vk_context = ContextVK::Cast(*context);
const auto [result, surface_caps] =
vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(surface_.get());
if (result != vk::Result::eSuccess) {
return std::nullopt;
}
// From the spec: `currentExtent` is the current width and height of the
// surface, or the special value (0xFFFFFFFF, 0xFFFFFFFF) indicating that the
// surface size will be determined by the extent of a swapchain targeting the
// surface.
constexpr uint32_t kCurrentExtentsPlaceholder = 0xFFFFFFFF;
if (surface_caps.currentExtent.width == kCurrentExtentsPlaceholder ||
surface_caps.currentExtent.height == kCurrentExtentsPlaceholder) {
return std::nullopt;
}
return ISize::MakeWH(surface_caps.currentExtent.width,
surface_caps.currentExtent.height);
}
bool KHRSwapchainImplVK::IsValid() const {
return is_valid_;
}

View File

@ -64,6 +64,8 @@ class KHRSwapchainImplVK final
void AddFinalCommandBuffer(std::shared_ptr<CommandBuffer> cmd_buffer);
std::optional<ISize> GetCurrentUnderlyingSurfaceSize() const;
private:
std::weak_ptr<Context> context_;
vk::UniqueSurfaceKHR surface_;

View File

@ -4,6 +4,7 @@
#include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h"
#include "flutter/fml/build_config.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h"
@ -45,6 +46,11 @@ void KHRSwapchainVK::AddFinalCommandBuffer(
}
std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() {
return AcquireNextDrawable(0u);
}
std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable(
size_t resize_retry_count) {
if (!IsValid()) {
return nullptr;
}
@ -56,6 +62,27 @@ std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() {
return std::move(result.surface);
}
// When the swapchain says its out-of-date, we attempt to read the underlying
// surface size and re-create the swapchain at that size automatically (subject
// to a specific number of retries). However, on some platforms, the surface
// size reported by the Vulkan API may be stale for several frames. Those
// platforms must explicitly set the swapchain size using out-of-band (to
// Vulkan) APIs.
//
// TODO(163070): Expose the API to set surface size in impeller.h
#if !FML_OS_ANDROID
constexpr const size_t kMaxResizeAttempts = 3u;
if (resize_retry_count == kMaxResizeAttempts) {
VALIDATION_LOG << "Attempted to resize the swapchain" << kMaxResizeAttempts
<< " time unsuccessfully. This platform likely doesn't "
"support returning the current swapchain extents and "
"must recreate the swapchain using the actual size.";
return nullptr;
}
size_ = impl_->GetCurrentUnderlyingSurfaceSize().value_or(size_);
#endif // !FML_OS_ANDROID
TRACE_EVENT0("impeller", "RecreateSwapchain");
// This swapchain implementation indicates that it is out of date. Tear it
@ -81,7 +108,7 @@ std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() {
//----------------------------------------------------------------------------
/// We managed to recreate the swapchain in the new configuration. Try again.
///
return AcquireNextDrawable();
return AcquireNextDrawable(resize_retry_count + 1);
}
vk::Format KHRSwapchainVK::GetSurfaceFormat() const {

View File

@ -56,6 +56,8 @@ class KHRSwapchainVK final : public SwapchainVK {
KHRSwapchainVK(const KHRSwapchainVK&) = delete;
KHRSwapchainVK& operator=(const KHRSwapchainVK&) = delete;
std::unique_ptr<Surface> AcquireNextDrawable(size_t resize_retry_count);
};
} // namespace impeller

View File

@ -13,7 +13,21 @@ embed_blob("embedded_icu_data") {
deps = []
}
impeller_component("interop") {
# The public C/C++ Impeller API.
impeller_component("interop_api") {
public = [
"impeller.h",
"impeller.hpp",
]
sources = [
"impeller_c.c",
"impeller_cc.cc",
]
}
# The common base used by all interop backends.
impeller_component("interop_base") {
sources = [
"color_filter.cc",
"color_filter.h",
@ -29,11 +43,6 @@ impeller_component("interop") {
"formats.h",
"image_filter.cc",
"image_filter.h",
"impeller.cc",
"impeller.h",
"impeller.hpp",
"impeller_c.c",
"impeller_cc.cc",
"mask_filter.cc",
"mask_filter.h",
"object.cc",
@ -59,6 +68,8 @@ impeller_component("interop") {
]
public_deps = [
":embedded_icu_data",
":interop_api",
"../../base",
"../../display_list",
"../../entity",
@ -67,8 +78,15 @@ impeller_component("interop") {
"//flutter/fml",
"//flutter/third_party/txt",
]
}
deps = [ ":embedded_icu_data" ]
# Wires up the public API entrypoints to the appropriate backends.
impeller_component("interop") {
sources = [ "impeller.cc" ]
public_deps = [
":interop_base",
"backend",
]
}
impeller_component("library") {
@ -79,12 +97,12 @@ impeller_component("library") {
deps = [ ":interop" ]
}
impeller_component("example") {
impeller_component("example_gl") {
target_type = "executable"
output_name = "impeller_interop_example"
output_name = "impeller_interop_example_gl"
sources = [ "example.c" ]
sources = [ "example_gl.c" ]
deps = [
":interop",
@ -92,6 +110,49 @@ impeller_component("example") {
]
}
impeller_component("example_mtl") {
target_type = "executable"
output_name = "impeller_interop_example_mtl"
sources = [ "example_mtl.m" ]
deps = [
":interop",
"//flutter/third_party/glfw",
]
frameworks = [ "QuartzCore.framework" ]
}
impeller_component("example_vk") {
target_type = "executable"
output_name = "impeller_interop_example_vk"
sources = [ "example_vk.c" ]
deps = [
":interop",
"//flutter/third_party/glfw",
]
}
group("example") {
deps = []
if (impeller_enable_opengles) {
deps += [ ":example_gl" ]
}
if (impeller_enable_metal) {
deps += [ ":example_mtl" ]
}
if (impeller_enable_vulkan) {
deps += [ ":example_vk" ]
}
}
impeller_component("interop_unittests") {
testonly = true
@ -133,6 +194,18 @@ zip_bundle("sdk") {
source = "impeller.hpp"
destination = "include/impeller.hpp"
},
{
source = "example_gl.c"
destination = "examples/example_gl.c"
},
{
source = "example_vk.c"
destination = "examples/example_vk.c"
},
{
source = "example_mtl.m"
destination = "examples/example_mtl.m"
},
]
if (is_mac) {

View File

@ -16,7 +16,7 @@ A single-header C API for 2D graphics and text rendering. [Impeller](../../READM
* The text layout and shaping engine along with the bundled ICU data tables brings the size up to ~2.5 MB.
* If the application does not need text layout and shaping, or can interface with an existing library on the target platform, it is recommended to generate the SDK without built-in support for typography.
* **Performant**
* Built to perform the best when using a modern graphics API like Metal or Vulkan (not all may be available to start) and when running on mobile tiler GPUs like the ones found in smartphones and AppleSilicon/ARM desktops.
* Built to perform the best when using a modern graphics API like Metal or Vulkan and when running on mobile tiler GPUs like the ones found in smartphones and AppleSilicon/ARM desktops.
* Impeller does need a GPU. Performance will likely be inadequate for interactive use cases when using software rendering. Software rendering can be enabled using projects like SwiftShader, Angle, LLVMPipe, etc… If you are using software rendering in your projects, restrict its use to testing on CI. Impeller will likely never have a dedicated software renderer.
# Prebuilt Artifacts
@ -79,7 +79,7 @@ window.Draw(dl);
### Standalone
A fully functional example of using Impeller to draw using GLFW is available in [`example.c`](example.c). This example is also present in the `impeller_sdk.zip` [prebuilts](#prebuilt-artifacts) along with necessary artifacts.
A fully functional example of using Impeller to draw using GLFW is available in [`example_gl.c`](example_gl.c), [`example_vk.c`](example_vk.c) and [`example_mtl.c`](example_mtl.c). This example is also present in the `impeller_sdk.zip` [prebuilts](#prebuilt-artifacts) along with necessary artifacts.
### CMake

View File

@ -0,0 +1,21 @@
# 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.
import("//flutter/impeller/tools/impeller.gni")
group("backend") {
public_deps = []
if (impeller_enable_metal) {
public_deps += [ "metal" ]
}
if (impeller_enable_opengles) {
public_deps += [ "gles" ]
}
if (impeller_enable_vulkan) {
public_deps += [ "vulkan" ]
}
}

View File

@ -0,0 +1,17 @@
# 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.
import("//flutter/impeller/tools/impeller.gni")
impeller_component("gles") {
public_deps = [ "../../:interop_base" ]
sources = [
"context_gles.cc",
"context_gles.h",
"reactor_worker_gles.cc",
"reactor_worker_gles.h",
"surface_gles.cc",
"surface_gles.h",
]
}

View File

@ -0,0 +1,66 @@
// 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 "impeller/toolkit/interop/backend/gles/context_gles.h"
#include "impeller/base/validation.h"
#include "impeller/entity/gles/entity_shaders_gles.h"
#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
#include "impeller/renderer/backend/gles/context_gles.h"
namespace impeller::interop {
ScopedObject<Context> ContextGLES::Create(
std::function<void*(const char* gl_proc_name)> proc_address_callback) {
auto proc_table = std::make_unique<ProcTableGLES>(
impeller::ProcTableGLES(std::move(proc_address_callback)));
if (!proc_table || !proc_table->IsValid()) {
VALIDATION_LOG << "Could not create valid OpenGL ES proc. table.";
return {};
}
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
std::make_shared<fml::NonOwnedMapping>(
impeller_entity_shaders_gles_data,
impeller_entity_shaders_gles_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_framebuffer_blend_shaders_gles_data,
impeller_framebuffer_blend_shaders_gles_length),
};
auto impeller_context = impeller::ContextGLES::Create(std::move(proc_table),
shader_mappings, false);
if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context.";
return {};
}
auto reactor_worker = std::make_shared<ReactorWorkerGLES>();
auto worker_id = impeller_context->AddReactorWorker(reactor_worker);
if (!worker_id.has_value()) {
VALIDATION_LOG << "Could not add reactor worker.";
return {};
}
return Create(std::move(impeller_context), std::move(reactor_worker));
}
ScopedObject<Context> ContextGLES::Create(
std::shared_ptr<impeller::Context> impeller_context,
std::shared_ptr<ReactorWorkerGLES> worker) {
// Can't call Create because of private constructor. Adopt the raw pointer
// instead.
auto context = Adopt<Context>(
new ContextGLES(std::move(impeller_context), std::move(worker)));
if (!context->IsValid()) {
VALIDATION_LOG << "Could not create valid context.";
return {};
}
return context;
}
ContextGLES::ContextGLES(std::shared_ptr<impeller::Context> context,
std::shared_ptr<ReactorWorkerGLES> worker)
: Context(std::move(context)), worker_(std::move(worker)) {}
ContextGLES::~ContextGLES() = default;
} // namespace impeller::interop

View File

@ -0,0 +1,43 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_CONTEXT_GLES_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_CONTEXT_GLES_H_
#include <functional>
#include <memory>
#include "impeller/toolkit/interop/backend/gles/reactor_worker_gles.h"
#include "impeller/toolkit/interop/context.h"
namespace impeller::interop {
class ContextGLES final : public Context {
public:
static ScopedObject<Context> Create(
std::function<void*(const char* gl_proc_name)> proc_address_callback);
static ScopedObject<Context> Create(
std::shared_ptr<impeller::Context> context,
std::shared_ptr<ReactorWorkerGLES> worker = nullptr);
ContextGLES();
// |Context|
~ContextGLES() override;
ContextGLES(const ContextGLES&) = delete;
ContextGLES& operator=(const ContextGLES&) = delete;
private:
std::shared_ptr<ReactorWorkerGLES> worker_;
ContextGLES(std::shared_ptr<impeller::Context> context,
std::shared_ptr<ReactorWorkerGLES> worker);
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_CONTEXT_GLES_H_

View File

@ -0,0 +1,19 @@
// 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 "impeller/toolkit/interop/backend/gles/reactor_worker_gles.h"
namespace impeller::interop {
ReactorWorkerGLES::ReactorWorkerGLES()
: thread_id_(std::this_thread::get_id()) {}
ReactorWorkerGLES::~ReactorWorkerGLES() = default;
bool ReactorWorkerGLES::CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const {
return thread_id_ == std::this_thread::get_id();
}
} // namespace impeller::interop

View File

@ -0,0 +1,33 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_REACTOR_WORKER_GLES_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_REACTOR_WORKER_GLES_H_
#include "impeller/renderer/backend/gles/reactor_gles.h"
namespace impeller::interop {
class ReactorWorkerGLES final : public ReactorGLES::Worker {
public:
ReactorWorkerGLES();
// |ReactorGLES::Worker|
~ReactorWorkerGLES() override;
// |ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const override;
private:
std::thread::id thread_id_;
ReactorWorkerGLES(const ReactorWorkerGLES&) = delete;
ReactorWorkerGLES& operator=(const ReactorWorkerGLES&) = delete;
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_REACTOR_WORKER_GLES_H_

View File

@ -0,0 +1,29 @@
// 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 "impeller/toolkit/interop/backend/gles/surface_gles.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
namespace impeller::interop {
SurfaceGLES::SurfaceGLES(Context& context,
uint64_t fbo,
PixelFormat color_format,
ISize size)
: SurfaceGLES(context,
impeller::SurfaceGLES::WrapFBO(
context.GetContext(),
[]() { return true; },
fbo,
color_format,
size)) {}
SurfaceGLES::SurfaceGLES(Context& context,
std::shared_ptr<impeller::Surface> surface)
: Surface(context, std::move(surface)) {}
SurfaceGLES::~SurfaceGLES() = default;
} // namespace impeller::interop

View File

@ -0,0 +1,31 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_SURFACE_GLES_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_SURFACE_GLES_H_
#include "impeller/toolkit/interop/surface.h"
namespace impeller::interop {
class SurfaceGLES final : public Surface {
public:
SurfaceGLES(Context& context,
uint64_t fbo,
PixelFormat color_format,
ISize size);
SurfaceGLES(Context& context, std::shared_ptr<impeller::Surface> surface);
// |Surface|
~SurfaceGLES();
SurfaceGLES(const SurfaceGLES&) = delete;
SurfaceGLES& operator=(const SurfaceGLES&) = delete;
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_SURFACE_GLES_H_

View File

@ -0,0 +1,15 @@
# 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.
import("//flutter/impeller/tools/impeller.gni")
impeller_component("metal") {
public_deps = [ "../../:interop_base" ]
sources = [
"context_mtl.h",
"context_mtl.mm",
"surface_mtl.h",
"surface_mtl.mm",
]
}

View File

@ -0,0 +1,37 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_CONTEXT_MTL_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_CONTEXT_MTL_H_
#include "impeller/renderer/backend/metal/swapchain_transients_mtl.h"
#include "impeller/toolkit/interop/context.h"
namespace impeller::interop {
class ContextMTL final : public Context {
public:
static ScopedObject<Context> Create();
static ScopedObject<Context> Create(
const std::shared_ptr<impeller::Context>& context);
// |Context|
~ContextMTL() override;
ContextMTL(const ContextMTL&) = delete;
ContextMTL& operator=(const ContextMTL&) = delete;
const std::shared_ptr<SwapchainTransientsMTL>& GetSwapchainTransients() const;
private:
std::shared_ptr<SwapchainTransientsMTL> swapchain_transients_;
explicit ContextMTL(const std::shared_ptr<impeller::Context>& context);
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_CONTEXT_MTL_H_

View File

@ -0,0 +1,66 @@
// 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 "impeller/toolkit/interop/backend/metal/context_mtl.h"
#include "impeller/base/validation.h"
#include "impeller/entity/mtl/entity_shaders.h"
#include "impeller/entity/mtl/framebuffer_blend_shaders.h"
#include "impeller/entity/mtl/modern_shaders.h"
#include "impeller/renderer/backend/metal/context_mtl.h"
#include "impeller/renderer/mtl/compute_shaders.h"
namespace impeller::interop {
static std::vector<std::shared_ptr<fml::Mapping>>
CreateShaderLibraryMappings() {
return {std::make_shared<fml::NonOwnedMapping>(
impeller_entity_shaders_data, impeller_entity_shaders_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_modern_shaders_data, impeller_modern_shaders_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_framebuffer_blend_shaders_data,
impeller_framebuffer_blend_shaders_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_compute_shaders_data, impeller_compute_shaders_length)};
}
ScopedObject<Context> ContextMTL::Create() {
auto impeller_context =
impeller::ContextMTL::Create(CreateShaderLibraryMappings(), //
std::make_shared<fml::SyncSwitch>(), //
"Impeller" //
);
if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context.";
return {};
}
return Create(std::move(impeller_context));
}
ScopedObject<Context> ContextMTL::Create(
const std::shared_ptr<impeller::Context>& impeller_context) {
// Can't call Create because of private constructor. Adopt the raw pointer
// instead.
auto context = Adopt<Context>(new ContextMTL(impeller_context));
if (!context->IsValid()) {
VALIDATION_LOG << " Could not create valid context.";
return {};
}
return context;
}
ContextMTL::ContextMTL(const std::shared_ptr<impeller::Context>& context)
: Context(context),
swapchain_transients_(std::make_shared<SwapchainTransientsMTL>(
context->GetResourceAllocator())) {}
ContextMTL::~ContextMTL() = default;
const std::shared_ptr<SwapchainTransientsMTL>&
ContextMTL::GetSwapchainTransients() const {
return swapchain_transients_;
}
} // namespace impeller::interop

View File

@ -0,0 +1,27 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_SURFACE_MTL_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_SURFACE_MTL_H_
#include "impeller/toolkit/interop/surface.h"
namespace impeller::interop {
class SurfaceMTL final : public Surface {
public:
SurfaceMTL(Context& context, void* metal_drawable);
SurfaceMTL(Context& context, std::shared_ptr<impeller::Surface> surface);
~SurfaceMTL();
SurfaceMTL(const SurfaceMTL&) = delete;
SurfaceMTL& operator=(const SurfaceMTL&) = delete;
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_SURFACE_MTL_H_

View File

@ -0,0 +1,26 @@
// 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 "impeller/toolkit/interop/backend/metal/surface_mtl.h"
#include "impeller/renderer/backend/metal/surface_mtl.h"
#include "impeller/toolkit/interop/backend/metal/context_mtl.h"
namespace impeller::interop {
SurfaceMTL::SurfaceMTL(Context& context, void* metal_drawable)
: SurfaceMTL(context,
impeller::SurfaceMTL::MakeFromMetalLayerDrawable(
context.GetContext(),
(__bridge id<CAMetalDrawable>)metal_drawable,
reinterpret_cast<interop::ContextMTL*>(&context)
->GetSwapchainTransients())) {}
SurfaceMTL::SurfaceMTL(Context& context,
std::shared_ptr<impeller::Surface> surface)
: Surface(context, std::move(surface)) {}
SurfaceMTL::~SurfaceMTL() = default;
} // namespace impeller::interop

View File

@ -0,0 +1,17 @@
# 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.
import("//flutter/impeller/tools/impeller.gni")
impeller_component("vulkan") {
public_deps = [ "../../:interop_base" ]
sources = [
"context_vk.cc",
"context_vk.h",
"surface_vk.cc",
"surface_vk.h",
"swapchain_vk.cc",
"swapchain_vk.h",
]
}

View File

@ -0,0 +1,119 @@
// 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 "impeller/toolkit/interop/backend/vulkan/context_vk.h"
#include "flutter/fml/paths.h"
#include "impeller/entity/vk/entity_shaders_vk.h"
#include "impeller/entity/vk/framebuffer_blend_shaders_vk.h"
#include "impeller/entity/vk/modern_shaders_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/vk/compute_shaders_vk.h"
namespace impeller::interop {
static std::vector<std::shared_ptr<fml::Mapping>>
CreateShaderLibraryMappings() {
return {
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_vk_data,
impeller_entity_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(impeller_modern_shaders_vk_data,
impeller_modern_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_framebuffer_blend_shaders_vk_data,
impeller_framebuffer_blend_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length),
};
}
// This bit is complicated by the fact that impeller::ContextVK::Settings takes
// a raw function pointer to the callback.
thread_local std::function<PFN_vkVoidFunction(VkInstance instance,
const char* proc_name)>
sContextVKProcAddressCallback;
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL ContextVKGetInstanceProcAddress(
VkInstance instance,
const char* proc_name) {
if (sContextVKProcAddressCallback) {
return sContextVKProcAddressCallback(instance, proc_name);
}
return nullptr;
}
ScopedObject<Context> ContextVK::Create(const Settings& settings) {
if (!settings.IsValid()) {
VALIDATION_LOG << "Invalid settings for Vulkan context creation.";
return {};
}
impeller::ContextVK::Settings impeller_settings;
impeller_settings.shader_libraries_data = CreateShaderLibraryMappings();
impeller_settings.cache_directory = fml::paths::GetCachesDirectory();
impeller_settings.enable_validation = true;
sContextVKProcAddressCallback = settings.instance_proc_address_callback;
impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress;
auto impeller_context =
impeller::ContextVK::Create(std::move(impeller_settings));
sContextVKProcAddressCallback = nullptr;
if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context.";
return {};
}
return Create(std::move(impeller_context));
}
ScopedObject<Context> ContextVK::Create(
std::shared_ptr<impeller::Context> impeller_context) {
// Can't call Create because of private constructor. Adopt the raw pointer
// instead.
auto context = Adopt<Context>(new ContextVK(std::move(impeller_context)));
if (!context->IsValid()) {
VALIDATION_LOG << " Could not create valid context.";
return {};
}
return context;
}
ContextVK::ContextVK(std::shared_ptr<impeller::Context> context)
: Context(std::move(context)) {}
ContextVK::~ContextVK() = default;
ContextVK::Settings::Settings(const ImpellerContextVulkanSettings& settings)
: enable_validation(settings.enable_vulkan_validation) {
instance_proc_address_callback =
[&settings](VkInstance instance,
const char* proc_name) -> PFN_vkVoidFunction {
if (settings.proc_address_callback) {
return reinterpret_cast<PFN_vkVoidFunction>(
settings.proc_address_callback(instance, proc_name,
settings.user_data));
}
return nullptr;
};
}
bool ContextVK::GetInfo(ImpellerContextVulkanInfo& info) const {
if (!IsValid()) {
return false;
}
const auto& context = impeller::ContextVK::Cast(*GetContext());
// NOLINTBEGIN(google-readability-casting)
info.vk_instance = reinterpret_cast<void*>(VkInstance(context.GetInstance()));
info.vk_physical_device =
reinterpret_cast<void*>(VkPhysicalDevice(context.GetPhysicalDevice()));
info.vk_logical_device =
reinterpret_cast<void*>(VkDevice(context.GetDevice()));
// NOLINTEND(google-readability-casting)
info.graphics_queue_family_index =
context.GetGraphicsQueue()->GetIndex().family;
info.graphics_queue_index = context.GetGraphicsQueue()->GetIndex().index;
return true;
}
bool ContextVK::Settings::IsValid() const {
return !!instance_proc_address_callback;
}
} // namespace impeller::interop

View File

@ -0,0 +1,47 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_CONTEXT_VK_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_CONTEXT_VK_H_
#include "impeller/renderer/backend/vulkan/vk.h"
#include "impeller/toolkit/interop/context.h"
#include "impeller/toolkit/interop/impeller.h"
namespace impeller::interop {
class ContextVK final : public Context {
public:
struct Settings {
std::function<PFN_vkVoidFunction(VkInstance instance,
const char* proc_name)>
instance_proc_address_callback;
bool enable_validation = false;
explicit Settings(const ImpellerContextVulkanSettings& settings);
bool IsValid() const;
};
static ScopedObject<Context> Create(const Settings& settings);
static ScopedObject<Context> Create(
std::shared_ptr<impeller::Context> context);
// |Context|
~ContextVK() override;
ContextVK(const ContextVK&) = delete;
ContextVK& operator=(const ContextVK&) = delete;
bool GetInfo(ImpellerContextVulkanInfo& info) const;
private:
explicit ContextVK(std::shared_ptr<impeller::Context> context);
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_CONTEXT_VK_H_

View File

@ -0,0 +1,15 @@
// 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 "impeller/toolkit/interop/backend/vulkan/surface_vk.h"
namespace impeller::interop {
SurfaceVK::SurfaceVK(Context& context,
std::shared_ptr<impeller::Surface> surface)
: Surface(context, std::move(surface)) {}
SurfaceVK::~SurfaceVK() = default;
} // namespace impeller::interop

View File

@ -0,0 +1,25 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_VK_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_VK_H_
#include "impeller/toolkit/interop/surface.h"
namespace impeller::interop {
class SurfaceVK final : public Surface {
public:
SurfaceVK(Context& context, std::shared_ptr<impeller::Surface> surface);
~SurfaceVK();
SurfaceVK(const SurfaceVK&) = delete;
SurfaceVK& operator=(const SurfaceVK&) = delete;
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_VK_H_

View File

@ -0,0 +1,65 @@
// 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 "impeller/toolkit/interop/backend/vulkan/swapchain_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
namespace impeller::interop {
SwapchainVK::SwapchainVK(Context& context, VkSurfaceKHR c_surface)
: context_(Ref(&context)) {
if (!context.IsVulkan()) {
VALIDATION_LOG << "Context is not Vulkan.";
return;
}
if (!c_surface) {
VALIDATION_LOG << "Invalid surface.";
return;
}
// Creating a unique object from a raw handle requires fetching the owner
// manually.
auto surface = vk::UniqueSurfaceKHR(
vk::SurfaceKHR{c_surface},
impeller::ContextVK::Cast(*context_->GetContext()).GetInstance());
auto swapchain = impeller::SwapchainVK::Create(context.GetContext(), //
std::move(surface), //
ISize::MakeWH(1, 1) //
);
if (!swapchain) {
VALIDATION_LOG << "Could not create Vulkan swapchain.";
return;
}
swapchain_ = std::move(swapchain);
}
SwapchainVK::~SwapchainVK() = default;
bool SwapchainVK::IsValid() const {
return swapchain_ && swapchain_->IsValid();
}
ScopedObject<SurfaceVK> SwapchainVK::AcquireNextSurface() {
if (!IsValid()) {
return nullptr;
}
auto impeller_surface = swapchain_->AcquireNextDrawable();
if (!impeller_surface) {
VALIDATION_LOG << "Could not acquire next drawable.";
return nullptr;
}
auto surface = Create<SurfaceVK>(*context_, std::move(impeller_surface));
if (!surface || !surface->IsValid()) {
VALIDATION_LOG << "Could not create valid surface.";
return nullptr;
}
return surface;
}
} // namespace impeller::interop

View File

@ -0,0 +1,39 @@
// 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_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SWAPCHAIN_VK_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SWAPCHAIN_VK_H_
#include "impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h"
#include "impeller/toolkit/interop/backend/vulkan/surface_vk.h"
#include "impeller/toolkit/interop/context.h"
#include "impeller/toolkit/interop/impeller.h"
#include "impeller/toolkit/interop/object.h"
namespace impeller::interop {
class SwapchainVK final
: public Object<SwapchainVK,
IMPELLER_INTERNAL_HANDLE_NAME(ImpellerVulkanSwapchain)> {
public:
SwapchainVK(Context& context, VkSurfaceKHR surface);
~SwapchainVK();
bool IsValid() const;
SwapchainVK(const SwapchainVK&) = delete;
SwapchainVK& operator=(const SwapchainVK&) = delete;
ScopedObject<SurfaceVK> AcquireNextSurface();
private:
ScopedObject<Context> context_;
std::shared_ptr<impeller::SwapchainVK> swapchain_;
};
} // namespace impeller::interop
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SWAPCHAIN_VK_H_

View File

@ -4,29 +4,12 @@
#include "impeller/toolkit/interop/context.h"
#include <thread>
#include "flutter/fml/logging.h"
#include "impeller/base/validation.h"
#include "impeller/typographer/backends/skia/typographer_context_skia.h"
#if IMPELLER_ENABLE_OPENGLES
#include "impeller/entity/gles/entity_shaders_gles.h"
#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#endif // IMPELLER_ENABLE_OPENGLES
namespace impeller::interop {
class Context::BackendData {
public:
virtual ~BackendData() = default;
};
Context::Context(std::shared_ptr<impeller::Context> context,
std::shared_ptr<BackendData> backend_data)
: context_(std::move(context), TypographerContextSkia::Make()),
backend_data_(std::move(backend_data)) {}
Context::Context(std::shared_ptr<impeller::Context> context)
: context_(std::move(context), TypographerContextSkia::Make()) {}
Context::~Context() = default;
@ -38,74 +21,27 @@ std::shared_ptr<impeller::Context> Context::GetContext() const {
return context_.GetContext();
}
#if IMPELLER_ENABLE_OPENGLES
class ReactorWorker final : public ReactorGLES::Worker,
public Context::BackendData {
public:
ReactorWorker() : thread_id_(std::this_thread::get_id()) {}
// |ReactorGLES::Worker|
~ReactorWorker() override = default;
// |ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const override {
return thread_id_ == std::this_thread::get_id();
}
private:
std::thread::id thread_id_;
FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
};
#endif // IMPELLER_ENABLE_OPENGLES
ScopedObject<Context> Context::CreateOpenGLES(
std::function<void*(const char* gl_proc_name)> proc_address_callback) {
#if IMPELLER_ENABLE_OPENGLES
auto proc_table = std::make_unique<ProcTableGLES>(
impeller::ProcTableGLES(std::move(proc_address_callback)));
if (!proc_table || !proc_table->IsValid()) {
VALIDATION_LOG << "Could not create valid OpenGL ES proc. table.";
return {};
}
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
std::make_shared<fml::NonOwnedMapping>(
impeller_entity_shaders_gles_data,
impeller_entity_shaders_gles_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_framebuffer_blend_shaders_gles_data,
impeller_framebuffer_blend_shaders_gles_length),
};
auto impeller_context =
ContextGLES::Create(std::move(proc_table), shader_mappings, false);
if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context.";
return {};
}
auto reactor_worker = std::make_shared<ReactorWorker>();
auto worker_id = impeller_context->AddReactorWorker(reactor_worker);
if (!worker_id.has_value()) {
VALIDATION_LOG << "Could not add reactor worker.";
return {};
}
auto context =
Create<Context>(std::move(impeller_context), std::move(reactor_worker));
if (!context->IsValid()) {
VALIDATION_LOG << "Could not create valid context.";
return {};
}
return context;
#else // IMPELLER_ENABLE_OPENGLES
VALIDATION_LOG << "This build does not support OpenGL ES contexts.";
return {};
#endif // IMPELLER_ENABLE_OPENGLES
}
AiksContext& Context::GetAiksContext() {
return context_;
}
bool Context::IsBackend(impeller::Context::BackendType type) const {
if (!IsValid()) {
return false;
}
return GetContext()->GetBackendType() == type;
}
bool Context::IsGL() const {
return IsBackend(impeller::Context::BackendType::kOpenGLES);
}
bool Context::IsMetal() const {
return IsBackend(impeller::Context::BackendType::kMetal);
}
bool Context::IsVulkan() const {
return IsBackend(impeller::Context::BackendType::kVulkan);
}
} // namespace impeller::interop

View File

@ -5,8 +5,6 @@
#ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_CONTEXT_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_CONTEXT_H_
#include <functional>
#include "impeller/display_list/aiks_context.h"
#include "impeller/renderer/context.h"
#include "impeller/toolkit/interop/impeller.h"
@ -14,17 +12,9 @@
namespace impeller::interop {
class Context final
class Context
: public Object<Context, IMPELLER_INTERNAL_HANDLE_NAME(ImpellerContext)> {
public:
class BackendData;
static ScopedObject<Context> CreateOpenGLES(
std::function<void*(const char* gl_proc_name)> proc_address_callback);
explicit Context(std::shared_ptr<impeller::Context> context,
std::shared_ptr<BackendData> backend_data);
~Context() override;
Context(const Context&) = delete;
@ -37,9 +27,19 @@ class Context final
AiksContext& GetAiksContext();
bool IsBackend(impeller::Context::BackendType type) const;
bool IsGL() const;
bool IsMetal() const;
bool IsVulkan() const;
protected:
explicit Context(std::shared_ptr<impeller::Context> context);
private:
impeller::AiksContext context_;
std::shared_ptr<BackendData> backend_data_;
};
} // namespace impeller::interop

View File

@ -23,10 +23,19 @@ int main(int argc, char const* argv[]) {
[[maybe_unused]] int result = glfwInit();
assert(result == GLFW_TRUE);
if (glfwGetPlatform() == GLFW_PLATFORM_COCOA) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr,
"OpenGL(ES) is not available on macOS. Please use Metal or Vulkan "
"instead.\n");
fflush(stderr);
return -1;
}
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
GLFWwindow* window =
glfwCreateWindow(800, 600, "Impeller Example", NULL, NULL);
glfwCreateWindow(800, 600, "Impeller Example (OpenGL)", NULL, NULL);
assert(window != NULL);
int framebuffer_width, framebuffer_height;

View File

@ -0,0 +1,110 @@
// 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 <assert.h>
#include <stdio.h>
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
#define GLFW_EXPOSE_NATIVE_COCOA
#import "GLFW/glfw3native.h"
#include "impeller.h"
#include <AppKit/AppKit.h>
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
void GLFWErrorCallback(int error, const char* description) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr, "GLFW Error (%d): %s\n", error, description);
fflush(stderr);
}
int main(int argc, char const* argv[]) {
glfwSetErrorCallback(GLFWErrorCallback);
[[maybe_unused]] int result = glfwInit();
assert(result == GLFW_TRUE);
if (glfwGetPlatform() != GLFW_PLATFORM_COCOA) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr,
"Metal is only available on macOS. Please try either Vulkan or "
"OpenGL (ES).\n");
fflush(stderr);
return -1;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(800, 600, "Impeller Example (Metal)", NULL, NULL);
assert(window != NULL);
int framebuffer_width, framebuffer_height;
glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
ImpellerContext context = ImpellerContextCreateMetalNew(IMPELLER_VERSION);
assert(context != NULL);
// This example assumes Automatic Reference Counting (ARC) in Objective-C is
// enabled.
NSWindow* cocoa_window = glfwGetCocoaWindow(window);
assert(cocoa_window != NULL);
CAMetalLayer* layer = [CAMetalLayer layer];
layer.framebufferOnly = NO;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.device = MTLCreateSystemDefaultDevice();
cocoa_window.contentView.layer = layer;
cocoa_window.contentView.wantsLayer = YES;
ImpellerDisplayList dl = NULL;
{
ImpellerDisplayListBuilder builder = ImpellerDisplayListBuilderNew(NULL);
ImpellerPaint paint = ImpellerPaintNew();
// Clear the background to a white color.
ImpellerColor clear_color = {1.0, 1.0, 1.0, 1.0};
ImpellerPaintSetColor(paint, &clear_color);
ImpellerDisplayListBuilderDrawPaint(builder, paint);
// Draw a red box.
ImpellerColor box_color = {1.0, 0.0, 0.0, 1.0};
ImpellerPaintSetColor(paint, &box_color);
ImpellerRect box_rect = {10, 10, 100, 100};
ImpellerDisplayListBuilderDrawRect(builder, &box_rect, paint);
dl = ImpellerDisplayListBuilderCreateDisplayListNew(builder);
ImpellerPaintRelease(paint);
ImpellerDisplayListBuilderRelease(builder);
}
assert(dl != NULL);
while (!glfwWindowShouldClose(window)) {
glfwWaitEvents();
// React to window resizes.
layer.drawableSize = layer.bounds.size;
ImpellerSurface surface = ImpellerSurfaceCreateWrappedMetalDrawableNew(
context, (__bridge void*)layer.nextDrawable);
assert(surface != NULL);
ImpellerSurfaceDrawDisplayList(surface, dl);
ImpellerSurfacePresent(surface);
ImpellerSurfaceRelease(surface);
}
ImpellerDisplayListRelease(dl);
ImpellerContextRelease(context);
glfwMakeContextCurrent(NULL);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View File

@ -0,0 +1,125 @@
// 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 <assert.h>
#include <stdio.h>
#define GLFW_INCLUDE_VULKAN
#include "GLFW/glfw3.h"
#include "impeller.h"
void GLFWErrorCallback(int error, const char* description) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr, "GLFW Error (%d): %s\n", error, description);
fflush(stderr);
}
void* ProcAddressCallback(void* vulkan_instance,
const char* vulkan_proc_name,
void* user_data) {
return glfwGetInstanceProcAddress(vulkan_instance, vulkan_proc_name);
}
int main(int argc, char const* argv[]) {
glfwSetErrorCallback(GLFWErrorCallback);
[[maybe_unused]] int result = glfwInit();
assert(result == GLFW_TRUE);
if (!glfwVulkanSupported()) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr, "Vulkan is not supported on this platform.\n");
fflush(stderr);
return -1;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(800, 600, "Impeller Example (Vulkan)", NULL, NULL);
assert(window != NULL);
ImpellerContextVulkanSettings vulkan_settings = {};
vulkan_settings.proc_address_callback = &ProcAddressCallback;
vulkan_settings.enable_vulkan_validation = true;
ImpellerContext context =
ImpellerContextCreateVulkanNew(IMPELLER_VERSION, &vulkan_settings);
assert(context != NULL);
ImpellerContextVulkanInfo info = {};
[[maybe_unused]] bool info_result =
ImpellerContextGetVulkanInfo(context, &info);
assert(!!info_result);
if (glfwGetPhysicalDevicePresentationSupport(
info.vk_instance, info.vk_physical_device,
info.graphics_queue_family_index) != GLFW_TRUE) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr, "Queue does not support presentation.\n");
fflush(stderr);
return -1;
}
VkSurfaceKHR vulkan_surface_khr;
VkResult error = glfwCreateWindowSurface(info.vk_instance, window, NULL,
&vulkan_surface_khr);
if (error) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
fprintf(stderr, "Could not create Vulkan surface for presentation.\n");
fflush(stderr);
return -1;
}
int framebuffer_width, framebuffer_height;
glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
ImpellerVulkanSwapchain swapchain =
ImpellerVulkanSwapchainCreateNew(context, vulkan_surface_khr);
assert(swapchain != NULL);
ImpellerDisplayList dl = NULL;
{
ImpellerDisplayListBuilder builder = ImpellerDisplayListBuilderNew(NULL);
ImpellerPaint paint = ImpellerPaintNew();
// Clear the background to a white color.
ImpellerColor clear_color = {1.0, 1.0, 1.0, 1.0};
ImpellerPaintSetColor(paint, &clear_color);
ImpellerDisplayListBuilderDrawPaint(builder, paint);
// Draw a red box.
ImpellerColor box_color = {1.0, 0.0, 0.0, 1.0};
ImpellerPaintSetColor(paint, &box_color);
ImpellerRect box_rect = {10, 10, 100, 100};
ImpellerDisplayListBuilderDrawRect(builder, &box_rect, paint);
dl = ImpellerDisplayListBuilderCreateDisplayListNew(builder);
ImpellerPaintRelease(paint);
ImpellerDisplayListBuilderRelease(builder);
}
assert(dl != NULL);
while (!glfwWindowShouldClose(window)) {
glfwWaitEvents();
ImpellerSurface surface =
ImpellerVulkanSwapchainAcquireNextSurfaceNew(swapchain);
assert(surface != NULL);
ImpellerSurfaceDrawDisplayList(surface, dl);
ImpellerSurfacePresent(surface);
ImpellerSurfaceRelease(surface);
}
ImpellerDisplayListRelease(dl);
ImpellerVulkanSwapchainRelease(swapchain);
ImpellerContextRelease(context);
glfwMakeContextCurrent(NULL);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View File

@ -8,7 +8,6 @@
#include "flutter/fml/mapping.h"
#include "impeller/base/validation.h"
#include "impeller/core/texture.h"
#include "impeller/geometry/scalar.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
@ -31,6 +30,22 @@
#include "impeller/toolkit/interop/texture.h"
#include "impeller/toolkit/interop/typography_context.h"
#if IMPELLER_ENABLE_OPENGLES
#include "impeller/toolkit/interop/backend/gles/context_gles.h"
#include "impeller/toolkit/interop/backend/gles/surface_gles.h"
#endif // IMPELLER_ENABLE_OPENGLES
#if IMPELLER_ENABLE_METAL
#include "impeller/toolkit/interop/backend/metal/context_mtl.h"
#include "impeller/toolkit/interop/backend/metal/surface_mtl.h"
#endif // IMPELLER_ENABLE_METAL
#if IMPELLER_ENABLE_VULKAN
#include "impeller/toolkit/interop/backend/vulkan/context_vk.h"
#include "impeller/toolkit/interop/backend/vulkan/surface_vk.h"
#include "impeller/toolkit/interop/backend/vulkan/swapchain_vk.h"
#endif // IMPELLER_ENABLE_VULKAN
namespace impeller::interop {
#define DEFINE_PEER_GETTER(cxx_type, c_type) \
@ -52,6 +67,7 @@ DEFINE_PEER_GETTER(ParagraphStyle, ImpellerParagraphStyle);
DEFINE_PEER_GETTER(Path, ImpellerPath);
DEFINE_PEER_GETTER(PathBuilder, ImpellerPathBuilder);
DEFINE_PEER_GETTER(Surface, ImpellerSurface);
DEFINE_PEER_GETTER(SwapchainVK, ImpellerVulkanSwapchain);
DEFINE_PEER_GETTER(Texture, ImpellerTexture);
DEFINE_PEER_GETTER(TypographyContext, ImpellerTypographyContext);
@ -69,19 +85,27 @@ uint32_t ImpellerGetVersion() {
return IMPELLER_VERSION;
}
IMPELLER_EXTERN_C
ImpellerContext ImpellerContextCreateOpenGLESNew(
uint32_t version,
ImpellerProcAddressCallback gl_proc_address_callback,
void* gl_proc_address_callback_user_data) {
static bool CheckVersion(uint32_t version) {
if (version != IMPELLER_VERSION) {
VALIDATION_LOG << "This version of Impeller ("
<< GetVersionAsString(ImpellerGetVersion()) << ") "
<< "doesn't match the version the user expects ("
<< GetVersionAsString(version) << ").";
return false;
}
return true;
}
IMPELLER_EXTERN_C
ImpellerContext ImpellerContextCreateOpenGLESNew(
uint32_t version,
ImpellerProcAddressCallback gl_proc_address_callback,
void* gl_proc_address_callback_user_data) {
if (!CheckVersion(version)) {
return nullptr;
}
auto context = Context::CreateOpenGLES(
#if IMPELLER_ENABLE_OPENGLES
auto context = ContextGLES::Create(
[gl_proc_address_callback,
gl_proc_address_callback_user_data](const char* proc_name) -> void* {
return gl_proc_address_callback(proc_name,
@ -92,6 +116,47 @@ ImpellerContext ImpellerContextCreateOpenGLESNew(
return nullptr;
}
return context.Leak();
#else // IMPELLER_ENABLE_OPENGLES
VALIDATION_LOG << "OpenGLES not available.";
return nullptr;
#endif // IMPELLER_ENABLE_OPENGLES
}
IMPELLER_EXTERN_C ImpellerContext ImpellerContextCreateMetalNew(
uint32_t version) {
if (!CheckVersion(version)) {
return nullptr;
}
#if IMPELLER_ENABLE_METAL
auto context = ContextMTL::Create();
if (!context || !context->IsValid()) {
VALIDATION_LOG << "Could not create valid context.";
return nullptr;
}
return context.Leak();
#else // IMPELLER_ENABLE_METAL
VALIDATION_LOG << "Metal not available.";
return nullptr;
#endif // IMPELLER_ENABLE_METAL
}
IMPELLER_EXTERN_C ImpellerContext ImpellerContextCreateVulkanNew(
uint32_t version,
const ImpellerContextVulkanSettings* settings) {
if (!CheckVersion(version)) {
return nullptr;
}
#if IMPELLER_ENABLE_VULKAN
auto context = ContextVK::Create(ContextVK::Settings(*settings));
if (!context || !context->IsValid()) {
VALIDATION_LOG << "Could not create valid context.";
return nullptr;
}
return context.Leak();
#else // IMPELLER_ENABLE_VULKAN
VALIDATION_LOG << "Vulkan not available.";
return nullptr;
#endif // IMPELLER_ENABLE_VULKAN
}
IMPELLER_EXTERN_C
@ -105,7 +170,54 @@ void ImpellerContextRelease(ImpellerContext context) {
}
IMPELLER_EXTERN_C
ImpellerDisplayListBuilder ImpellerDisplayListBuilderNew(
bool ImpellerContextGetVulkanInfo(ImpellerContext IMPELLER_NONNULL context,
ImpellerContextVulkanInfo* out_vulkan_info) {
#if IMPELLER_ENABLE_VULKAN
if (!GetPeer(context)->IsVulkan()) {
VALIDATION_LOG << "Not a Vulkan context.";
return false;
}
return reinterpret_cast<ContextVK*>(GetPeer(context))
->GetInfo(*out_vulkan_info);
#else // IMPELLER_ENABLE_VULKAN
VALIDATION_LOG << "Vulkan not available.";
return nullptr;
#endif // IMPELLER_ENABLE_VULKAN
}
IMPELLER_EXTERN_C
ImpellerVulkanSwapchain ImpellerVulkanSwapchainCreateNew(
ImpellerContext context,
void* vulkan_surface_khr) {
#if IMPELLER_ENABLE_VULKAN
return Create<SwapchainVK>(
*GetPeer(context), //
reinterpret_cast<VkSurfaceKHR>(vulkan_surface_khr) //
)
.Leak();
#else // IMPELLER_ENABLE_VULKAN
VALIDATION_LOG << "Vulkan not available.";
return nullptr;
#endif // IMPELLER_ENABLE_VULKAN
}
IMPELLER_EXTERN_C
void ImpellerVulkanSwapchainRetain(ImpellerVulkanSwapchain swapchain) {
ObjectBase::SafeRetain(swapchain);
}
IMPELLER_EXTERN_C
void ImpellerVulkanSwapchainRelease(ImpellerVulkanSwapchain swapchain) {
ObjectBase::SafeRelease(swapchain);
}
IMPELLER_EXTERN_C
ImpellerSurface ImpellerVulkanSwapchainAcquireNextSurfaceNew(
ImpellerVulkanSwapchain swapchain) {
return GetPeer(swapchain)->AcquireNextSurface().Leak();
}
IMPELLER_EXTERN_C ImpellerDisplayListBuilder ImpellerDisplayListBuilderNew(
const ImpellerRect* cull_rect) {
return Create<DisplayListBuilder>(cull_rect).Leak();
}
@ -475,7 +587,7 @@ ImpellerTexture ImpellerTextureCreateWithContentsNew(
const ImpellerMapping* contents,
void* contents_on_release_user_data) {
TextureDescriptor desc;
desc.storage_mode = StorageMode::kDevicePrivate;
desc.storage_mode = StorageMode::kHostVisible;
desc.type = TextureType::kTexture2D;
desc.format = ToImpellerType(descriptor->pixel_format);
desc.size = ToImpellerType(descriptor->size);
@ -525,7 +637,8 @@ ImpellerTexture ImpellerTextureCreateWithOpenGLTextureHandleNew(
return nullptr;
}
const auto& impeller_context_gl = ContextGLES::Cast(*impeller_context);
const auto& impeller_context_gl =
impeller::ContextGLES::Cast(*impeller_context);
const auto& reactor = impeller_context_gl.GetReactor();
TextureDescriptor desc;
@ -608,15 +721,39 @@ ImpellerSurface ImpellerSurfaceCreateWrappedFBONew(ImpellerContext context,
uint64_t fbo,
ImpellerPixelFormat format,
const ImpellerISize* size) {
return Surface::WrapFBO(*GetPeer(context), //
fbo, //
ToImpellerType(format), //
ToImpellerType(*size)) //
#if IMPELLER_ENABLE_OPENGLES
if (!GetPeer(context)->IsGL()) {
VALIDATION_LOG << "Context is not OpenGL.";
return nullptr;
}
return Create<SurfaceGLES>(*GetPeer(context), //
fbo, //
ToImpellerType(format), //
ToImpellerType(*size)) //
.Leak();
#else // IMPELLER_ENABLE_OPENGLES
VALIDATION_LOG << "OpenGL unavailable.";
return nullptr;
#endif // IMPELLER_ENABLE_OPENGLES
}
IMPELLER_EXTERN_C
void ImpellerSurfaceRetain(ImpellerSurface surface) {
ImpellerSurface ImpellerSurfaceCreateWrappedMetalDrawableNew(
ImpellerContext context,
void* metal_drawable) {
#if IMPELLER_ENABLE_METAL
if (!GetPeer(context)->IsMetal()) {
VALIDATION_LOG << "Context is not Metal.";
return nullptr;
}
return Create<SurfaceMTL>(*GetPeer(context), metal_drawable).Leak();
#else // IMPELLER_ENABLE_METAL
VALIDATION_LOG << "Metal unavailable.";
return nullptr;
#endif // IMPELLER_ENABLE_METAL
}
IMPELLER_EXTERN_C void ImpellerSurfaceRetain(ImpellerSurface surface) {
ObjectBase::SafeRetain(surface);
}
@ -631,6 +768,11 @@ bool ImpellerSurfaceDrawDisplayList(ImpellerSurface surface,
return GetPeer(surface)->DrawDisplayList(*GetPeer(display_list));
}
IMPELLER_EXTERN_C
bool ImpellerSurfacePresent(ImpellerSurface surface) {
return GetPeer(surface)->Present();
}
IMPELLER_EXTERN_C
void ImpellerDisplayListBuilderDrawTexture(ImpellerDisplayListBuilder builder,
ImpellerTexture texture,

View File

@ -5,6 +5,8 @@
#ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_IMPELLER_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_IMPELLER_H_
// NOLINTBEGIN(google-objc-function-naming)
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -80,7 +82,7 @@ IMPELLER_EXTERN_C_BEGIN
#define IMPELLER_VERSION_VARIANT 1
#define IMPELLER_VERSION_MAJOR 1
#define IMPELLER_VERSION_MINOR 2
#define IMPELLER_VERSION_MINOR 3
#define IMPELLER_VERSION_PATCH 0
//------------------------------------------------------------------------------
@ -288,6 +290,18 @@ IMPELLER_DEFINE_HANDLE(ImpellerSurface);
///
IMPELLER_DEFINE_HANDLE(ImpellerTexture);
//------------------------------------------------------------------------------
/// The primary form of WSI when using a Vulkan context, these swapchains use
/// the `VK_KHR_surface` Vulkan extension.
///
/// Creating a swapchain is extremely expensive. One must be created at
/// application startup and re-used throughout the application lifecycle.
///
/// Swapchains are resilient to the underlying surfaces being resized. The
/// swapchain images will be re-created as necessary on-demand.
///
IMPELLER_DEFINE_HANDLE(ImpellerVulkanSwapchain);
//------------------------------------------------------------------------------
// Signatures
//------------------------------------------------------------------------------
@ -311,6 +325,16 @@ typedef void* IMPELLER_NULLABLE (*ImpellerProcAddressCallback)(
const char* IMPELLER_NONNULL proc_name,
void* IMPELLER_NULLABLE user_data);
//------------------------------------------------------------------------------
/// A callback used by Impeller to allow the user to resolve Vulkan function
/// pointers. A user supplied baton that is uninterpreted by Impeller is passed
/// back to the user in the callback.
///
typedef void* IMPELLER_NULLABLE (*ImpellerVulkanProcAddressCallback)(
void* IMPELLER_NULLABLE vulkan_instance,
const char* IMPELLER_NONNULL vulkan_proc_name,
void* IMPELLER_NULLABLE user_data);
//------------------------------------------------------------------------------
// Enumerations
// -----------------------------------------------------------------------------
@ -562,6 +586,20 @@ typedef struct ImpellerMapping {
ImpellerCallback IMPELLER_NULLABLE on_release;
} ImpellerMapping;
typedef struct ImpellerContextVulkanSettings {
void* IMPELLER_NULLABLE user_data;
ImpellerVulkanProcAddressCallback IMPELLER_NONNULL proc_address_callback;
bool enable_vulkan_validation;
} ImpellerContextVulkanSettings;
typedef struct ImpellerContextVulkanInfo {
void* IMPELLER_NULLABLE vk_instance;
void* IMPELLER_NULLABLE vk_physical_device;
void* IMPELLER_NULLABLE vk_logical_device;
uint32_t graphics_queue_family_index;
uint32_t graphics_queue_index;
} ImpellerContextVulkanInfo;
//------------------------------------------------------------------------------
// Version
//------------------------------------------------------------------------------
@ -625,6 +663,29 @@ ImpellerContextCreateOpenGLESNew(
ImpellerProcAddressCallback IMPELLER_NONNULL gl_proc_address_callback,
void* IMPELLER_NULLABLE gl_proc_address_callback_user_data);
//------------------------------------------------------------------------------
/// @brief Create a Metal context using the system default Metal device.
///
/// @param[in] version The version specified in the IMPELLER_VERSION macro.
///
/// @return The Metal context or NULL if one cannot be created.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerContext IMPELLER_NULLABLE
ImpellerContextCreateMetalNew(uint32_t version);
//------------------------------------------------------------------------------
/// @brief Create a Vulkan context using the provided Vulkan Settings.
///
/// @param[in] version The version specified in the IMPELLER_VERSION macro.
/// @param[in] settings The Vulkan settings.
///
/// @return The Vulkan context or NULL if one cannot be created.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerContext IMPELLER_NULLABLE
ImpellerContextCreateVulkanNew(
uint32_t version,
const ImpellerContextVulkanSettings* IMPELLER_NONNULL settings);
//------------------------------------------------------------------------------
/// @brief Retain a strong reference to the object. The object can be NULL
/// in which case this method is a no-op.
@ -643,6 +704,80 @@ void ImpellerContextRetain(ImpellerContext IMPELLER_NULLABLE context);
IMPELLER_EXPORT
void ImpellerContextRelease(ImpellerContext IMPELLER_NULLABLE context);
//------------------------------------------------------------------------------
/// @brief Get internal Vulkan handles managed by the given Vulkan context.
/// Ownership of the handles is still maintained by Impeller. This
/// accessor is just available so embedders can create resources
/// using the same device and instance as Impeller for interop.
///
/// @warning If the context is not a Vulkan context, False is returned with
/// the [out] argument unaffected.
///
/// @param[in] context The context
/// @param[out] out_vulkan_info The out vulkan information
///
/// @return If the Vulkan info could be fetched from the context.
///
IMPELLER_EXPORT
bool ImpellerContextGetVulkanInfo(
ImpellerContext IMPELLER_NONNULL context,
ImpellerContextVulkanInfo* IMPELLER_NONNULL out_vulkan_info);
//------------------------------------------------------------------------------
// Vulkan Swapchain
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// @brief Create a new Vulkan swapchain using a VkSurfaceKHR instance.
/// Ownership of the surface is transferred over to Impeller. The
/// Vulkan instance the surface is created from must the same as the
/// context provided.
///
/// @param[in] context The context. Must be a Vulkan context whose
/// instance is the same used to create the
/// surface passed into the next argument.
/// @param vulkan_surface_khr The vulkan surface.
///
/// @return The vulkan swapchain.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerVulkanSwapchain IMPELLER_NULLABLE
ImpellerVulkanSwapchainCreateNew(ImpellerContext IMPELLER_NONNULL context,
void* IMPELLER_NONNULL vulkan_surface_khr);
//------------------------------------------------------------------------------
/// @brief Retain a strong reference to the object. The object can be NULL
/// in which case this method is a no-op.
///
/// @param[in] swapchain The swapchain.
///
IMPELLER_EXPORT
void ImpellerVulkanSwapchainRetain(
ImpellerVulkanSwapchain IMPELLER_NULLABLE swapchain);
//------------------------------------------------------------------------------
/// @brief Release a previously retained reference to the object. The
/// object can be NULL in which case this method is a no-op.
///
/// @param[in] swapchain The swapchain.
///
IMPELLER_EXPORT
void ImpellerVulkanSwapchainRelease(
ImpellerVulkanSwapchain IMPELLER_NULLABLE swapchain);
//------------------------------------------------------------------------------
/// @brief A potentially blocking operation, acquires the next surface to
/// render to. Since this may block, surface acquisition must be
/// delayed for as long as possible to avoid an idle wait on the
/// CPU.
///
/// @param[in] swapchain The swapchain.
///
/// @return The surface if one could be obtained, NULL otherwise.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerSurface IMPELLER_NULLABLE
ImpellerVulkanSwapchainAcquireNextSurfaceNew(
ImpellerVulkanSwapchain IMPELLER_NONNULL swapchain);
//------------------------------------------------------------------------------
// Surface
//------------------------------------------------------------------------------
@ -662,10 +797,30 @@ void ImpellerContextRelease(ImpellerContext IMPELLER_NULLABLE context);
/// @return The surface if once can be created, NULL otherwise.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerSurface IMPELLER_NULLABLE
ImpellerSurfaceCreateWrappedFBONew(ImpellerContext IMPELLER_NULLABLE context,
ImpellerSurfaceCreateWrappedFBONew(ImpellerContext IMPELLER_NONNULL context,
uint64_t fbo,
ImpellerPixelFormat format,
const ImpellerISize* IMPELLER_NULLABLE size);
const ImpellerISize* IMPELLER_NONNULL size);
//------------------------------------------------------------------------------
/// @brief Create a surface by wrapping a Metal drawable. This is useful
/// during WSI when the drawable is the backing store of the Metal
/// layer being drawn to.
///
/// The Metal layer must be using the same device managed by the
/// underlying context.
///
/// @param[in] context The context. The Metal device managed by this
/// context must be the same used to create the
/// drawable that is being wrapped.
/// @param metal_drawable The drawable to wrap as a surface.
///
/// @return The surface if one could be wrapped, NULL otherwise.
///
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerSurface IMPELLER_NULLABLE
ImpellerSurfaceCreateWrappedMetalDrawableNew(
ImpellerContext IMPELLER_NONNULL context,
void* IMPELLER_NONNULL metal_drawable);
//------------------------------------------------------------------------------
/// @brief Retain a strong reference to the object. The object can be NULL
@ -704,9 +859,19 @@ void ImpellerSurfaceRelease(ImpellerSurface IMPELLER_NULLABLE surface);
///
IMPELLER_EXPORT
bool ImpellerSurfaceDrawDisplayList(
ImpellerSurface IMPELLER_NULLABLE surface,
ImpellerSurface IMPELLER_NONNULL surface,
ImpellerDisplayList IMPELLER_NONNULL display_list);
//------------------------------------------------------------------------------
/// @brief Present the surface to the underlying window system.
///
/// @param[in] surface The surface to present.
///
/// @return True if the surface could be presented.
///
IMPELLER_EXPORT
bool ImpellerSurfacePresent(ImpellerSurface IMPELLER_NONNULL surface);
//------------------------------------------------------------------------------
// Path
//------------------------------------------------------------------------------
@ -2423,4 +2588,6 @@ uint32_t ImpellerParagraphGetLineCount(
IMPELLER_EXTERN_C_END
// NOLINTEND(google-objc-function-naming)
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_IMPELLER_H_

View File

@ -25,7 +25,7 @@
// Tripping this assertion means that the C++ wrapper needs to be updated to
// account for impeller.h changes as necessary.
static_assert(IMPELLER_VERSION == IMPELLER_MAKE_VERSION(1, 1, 2, 0),
static_assert(IMPELLER_VERSION == IMPELLER_MAKE_VERSION(1, 1, 3, 0),
"C++ bindings must be for the same version as the C API.");
namespace IMPELLER_HPP_NAMESPACE {
@ -56,7 +56,10 @@ struct Proc {
PROC(ImpellerColorSourceCreateSweepGradientNew) \
PROC(ImpellerColorSourceRelease) \
PROC(ImpellerColorSourceRetain) \
PROC(ImpellerContextCreateMetalNew) \
PROC(ImpellerContextCreateOpenGLESNew) \
PROC(ImpellerContextCreateVulkanNew) \
PROC(ImpellerContextGetVulkanInfo) \
PROC(ImpellerContextRelease) \
PROC(ImpellerContextRetain) \
PROC(ImpellerDisplayListBuilderClipOval) \
@ -166,7 +169,9 @@ struct Proc {
PROC(ImpellerPathRelease) \
PROC(ImpellerPathRetain) \
PROC(ImpellerSurfaceCreateWrappedFBONew) \
PROC(ImpellerSurfaceCreateWrappedMetalDrawableNew) \
PROC(ImpellerSurfaceDrawDisplayList) \
PROC(ImpellerSurfacePresent) \
PROC(ImpellerSurfaceRelease) \
PROC(ImpellerSurfaceRetain) \
PROC(ImpellerTextureCreateWithContentsNew) \
@ -177,7 +182,11 @@ struct Proc {
PROC(ImpellerTypographyContextNew) \
PROC(ImpellerTypographyContextRegisterFont) \
PROC(ImpellerTypographyContextRelease) \
PROC(ImpellerTypographyContextRetain)
PROC(ImpellerTypographyContextRetain) \
PROC(ImpellerVulkanSwapchainAcquireNextSurfaceNew) \
PROC(ImpellerVulkanSwapchainCreateNew) \
PROC(ImpellerVulkanSwapchainRelease) \
PROC(ImpellerVulkanSwapchainRetain)
struct ProcTable {
bool Initialize(
@ -285,6 +294,7 @@ IMPELLER_HPP_DEFINE_TRAITS(ImpellerPathBuilder);
IMPELLER_HPP_DEFINE_TRAITS(ImpellerSurface);
IMPELLER_HPP_DEFINE_TRAITS(ImpellerTexture);
IMPELLER_HPP_DEFINE_TRAITS(ImpellerTypographyContext);
IMPELLER_HPP_DEFINE_TRAITS(ImpellerVulkanSwapchain);
#undef IMPELLER_HPP_DEFINE_TRAITS
@ -335,6 +345,13 @@ class Context final : public Object<ImpellerContext, ImpellerContextTraits> {
),
AdoptTag::kAdopt);
}
//----------------------------------------------------------------------------
/// @see ImpellerContextGetVulkanInfo
///
bool GetVulkanInfo(ImpellerContextVulkanInfo& info) const {
return gGlobalProcTable.ImpellerContextGetVulkanInfo(Get(), &info);
}
};
//------------------------------------------------------------------------------
@ -1132,13 +1149,60 @@ class Surface final : public Object<ImpellerSurface, ImpellerSurfaceTraits> {
AdoptTag::kAdopt);
}
//----------------------------------------------------------------------------
/// @see ImpellerSurfaceCreateWrappedMetalDrawableNew
///
static Surface WrapMetalDrawable(const Context& context,
void* metal_drawable) {
return Surface(
gGlobalProcTable.ImpellerSurfaceCreateWrappedMetalDrawableNew(
context.Get(), metal_drawable),
AdoptTag::kAdopt);
}
//----------------------------------------------------------------------------
/// @see ImpellerSurfaceDrawDisplayList
///
bool Draw(const DisplayList& display_list) {
bool Draw(const DisplayList& display_list) const {
return gGlobalProcTable.ImpellerSurfaceDrawDisplayList(Get(),
display_list.Get());
}
//----------------------------------------------------------------------------
/// @see ImpellerSurfacePresent
///
bool Present() const {
return gGlobalProcTable.ImpellerSurfacePresent(Get());
}
};
//------------------------------------------------------------------------------
/// @see ImpellerVulkanSwapchain
///
class VulkanSwapchain final
: public Object<ImpellerVulkanSwapchain, ImpellerVulkanSwapchainTraits> {
public:
VulkanSwapchain(ImpellerVulkanSwapchain swapchain, AdoptTag tag)
: Object(swapchain, tag) {}
//----------------------------------------------------------------------------
/// @see ImpellerVulkanSwapchainCreateNew
///
static VulkanSwapchain Create(const Context& context,
void* vulkan_surface_khr) {
return VulkanSwapchain(gGlobalProcTable.ImpellerVulkanSwapchainCreateNew(
context.Get(), vulkan_surface_khr),
AdoptTag::kAdopt);
}
//----------------------------------------------------------------------------
/// @see ImpellerVulkanSwapchainAcquireNextSurfaceNew
///
Surface AcquireNextSurface() const {
return Surface(
gGlobalProcTable.ImpellerVulkanSwapchainAcquireNextSurfaceNew(Get()),
AdoptTag::kAdopt);
}
};
//------------------------------------------------------------------------------

View File

@ -24,7 +24,7 @@
namespace impeller::interop::testing {
using InteropPlaygroundTest = PlaygroundTest;
INSTANTIATE_OPENGLES_PLAYGROUND_SUITE(InteropPlaygroundTest);
INSTANTIATE_PLAYGROUND_SUITE(InteropPlaygroundTest);
TEST_P(InteropPlaygroundTest, CanCreateContext) {
auto context = CreateContext();
@ -43,6 +43,11 @@ TEST_P(InteropPlaygroundTest, CanCreateDisplayListBuilder) {
}
TEST_P(InteropPlaygroundTest, CanCreateSurface) {
if (GetBackend() != PlaygroundBackend::kOpenGLES) {
GTEST_SKIP()
<< "This test checks wrapping FBOs which is an OpenGL ES only call.";
return;
}
auto context = CreateContext();
ASSERT_TRUE(context);
const auto window_size = GetWindowSize();

View File

@ -6,13 +6,28 @@
#include "impeller/toolkit/interop/impeller.hpp"
#if IMPELLER_ENABLE_METAL
#include "impeller/toolkit/interop/backend/metal/context_mtl.h"
#include "impeller/toolkit/interop/backend/metal/surface_mtl.h"
#endif // IMPELLER_ENABLE_METAL
#if IMPELLER_ENABLE_OPENGLES
#include "impeller/toolkit/interop/backend/gles/context_gles.h"
#include "impeller/toolkit/interop/backend/gles/surface_gles.h"
#endif // IMPELLER_ENABLE_METAL
#if IMPELLER_ENABLE_VULKAN
#include "impeller/toolkit/interop/backend/vulkan/context_vk.h"
#include "impeller/toolkit/interop/backend/vulkan/surface_vk.h"
#endif // IMPELLER_ENABLE_VULKAN
namespace IMPELLER_HPP_NAMESPACE {
ProcTable gGlobalProcTable;
} // namespace IMPELLER_HPP_NAMESPACE
namespace impeller::interop::testing {
PlaygroundTest::PlaygroundTest() {
static void SetupImpellerHPPProcTableOnce() {
static std::once_flag sOnceFlag;
std::call_once(sOnceFlag, []() {
std::map<std::string, void*> proc_map;
@ -25,6 +40,10 @@ PlaygroundTest::PlaygroundTest() {
});
}
PlaygroundTest::PlaygroundTest() {
SetupImpellerHPPProcTableOnce();
}
PlaygroundTest::~PlaygroundTest() = default;
// |PlaygroundTest|
@ -40,8 +59,8 @@ void PlaygroundTest::TearDown() {
ScopedObject<Context> PlaygroundTest::CreateContext() const {
switch (GetBackend()) {
case PlaygroundBackend::kMetal:
FML_CHECK(false) << "Metal not yet implemented.";
return nullptr;
return Adopt<Context>(
ImpellerContextCreateMetalNew(ImpellerGetVersion()));
case PlaygroundBackend::kOpenGLES: {
Playground::GLProcAddressResolver playground_gl_proc_address_callback =
CreateGLProcAddressResolver();
@ -55,32 +74,103 @@ ScopedObject<Context> PlaygroundTest::CreateContext() const {
&playground_gl_proc_address_callback));
}
case PlaygroundBackend::kVulkan:
FML_CHECK(false) << "Vulkan not yet implemented.";
ImpellerContextVulkanSettings settings = {};
struct UserData {
Playground::VKProcAddressResolver resolver;
} user_data;
user_data.resolver = CreateVKProcAddressResolver();
settings.user_data = &user_data;
settings.enable_vulkan_validation = switches_.enable_vulkan_validation;
settings.proc_address_callback = [](void* instance, //
const char* proc_name, //
void* user_data //
) -> void* {
auto resolver = reinterpret_cast<UserData*>(user_data)->resolver;
if (resolver) {
return resolver(instance, proc_name);
} else {
return nullptr;
}
};
return Adopt<Context>(
ImpellerContextCreateVulkanNew(ImpellerGetVersion(), &settings));
}
FML_UNREACHABLE();
}
static ScopedObject<Surface> CreateSharedSurface(
PlaygroundBackend backend,
Context& context,
std::shared_ptr<impeller::Surface> shared_surface) {
switch (backend) {
#if IMPELLER_ENABLE_METAL
case PlaygroundBackend::kMetal:
return Adopt<Surface>(new SurfaceMTL(context, std::move(shared_surface)));
#endif
#if IMPELLER_ENABLE_OPENGLES
case PlaygroundBackend::kOpenGLES:
return Adopt<Surface>(
new SurfaceGLES(context, std::move(shared_surface)));
#endif
#if IMPELLER_ENABLE_VULKAN
case PlaygroundBackend::kVulkan:
return Adopt<Surface>(new SurfaceVK(context, std::move(shared_surface)));
#endif
default:
return nullptr;
}
FML_UNREACHABLE();
}
bool PlaygroundTest::OpenPlaygroundHere(InteropPlaygroundCallback callback) {
auto context = GetInteropContext();
if (!context) {
auto interop_context = GetInteropContext();
if (!interop_context) {
return false;
}
return Playground::OpenPlaygroundHere([&](RenderTarget& target) -> bool {
auto impeller_surface = std::make_shared<impeller::Surface>(target);
auto surface = Create<Surface>(*context.Get(), impeller_surface);
auto surface = CreateSharedSurface(GetBackend(), //
*interop_context.Get(), //
std::move(impeller_surface) //
);
if (!surface) {
VALIDATION_LOG << "Could not wrap test surface as an interop surface.";
return false;
}
return callback(context, surface);
return callback(interop_context, surface);
});
}
static ScopedObject<Context> CreateSharedContext(
PlaygroundBackend backend,
std::shared_ptr<impeller::Context> shared_context) {
switch (backend) {
#if IMPELLER_ENABLE_METAL
case PlaygroundBackend::kMetal:
return ContextMTL::Create(shared_context);
#endif
#if IMPELLER_ENABLE_OPENGLES
case PlaygroundBackend::kOpenGLES:
return ContextGLES::Create(std::move(shared_context));
#endif
#if IMPELLER_ENABLE_VULKAN
case PlaygroundBackend::kVulkan:
return ContextVK::Create(std::move(shared_context));
#endif
default:
return nullptr;
}
FML_UNREACHABLE();
}
ScopedObject<Context> PlaygroundTest::GetInteropContext() {
if (interop_context_) {
return interop_context_;
}
auto context = Create<Context>(GetContext(), nullptr);
auto context = CreateSharedContext(GetBackend(), GetContext());
if (!context) {
return nullptr;
}

View File

@ -7,7 +7,6 @@
#include "impeller/base/validation.h"
#include "impeller/display_list/aiks_context.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
#include "impeller/toolkit/interop/formats.h"
namespace impeller::interop {
@ -20,31 +19,6 @@ Surface::Surface(Context& context, std::shared_ptr<impeller::Surface> surface)
Surface::~Surface() = default;
ScopedObject<Surface> Surface::WrapFBO(Context& context,
uint64_t fbo,
PixelFormat color_format,
ISize size) {
if (context.GetContext()->GetBackendType() !=
impeller::Context::BackendType::kOpenGLES) {
VALIDATION_LOG << "Context is not OpenGL ES based.";
return nullptr;
}
auto impeller_surface = impeller::SurfaceGLES::WrapFBO(
context.GetContext(), []() { return true; }, fbo, color_format, size);
if (!impeller_surface || !impeller_surface->IsValid()) {
VALIDATION_LOG << "Could not wrap FBO as a surface";
return nullptr;
}
auto surface = Create<Surface>(context, std::move(impeller_surface));
if (!surface->IsValid()) {
VALIDATION_LOG << "Could not create valid surface.";
return nullptr;
}
return surface;
}
bool Surface::IsValid() const {
return is_valid_;
}
@ -68,4 +42,11 @@ bool Surface::DrawDisplayList(const DisplayList& dl) const {
return result;
}
bool Surface::Present() const {
if (!IsValid()) {
return false;
}
return surface_->Present();
}
} // namespace impeller::interop

View File

@ -15,17 +15,9 @@
namespace impeller::interop {
class Surface final
class Surface
: public Object<Surface, IMPELLER_INTERNAL_HANDLE_NAME(ImpellerSurface)> {
public:
static ScopedObject<Surface> WrapFBO(Context& context,
uint64_t fbo,
PixelFormat color_format,
ISize size);
explicit Surface(Context& context,
std::shared_ptr<impeller::Surface> surface);
~Surface() override;
Surface(const Surface&) = delete;
@ -36,6 +28,12 @@ class Surface final
bool DrawDisplayList(const DisplayList& dl) const;
bool Present() const;
protected:
explicit Surface(Context& context,
std::shared_ptr<impeller::Surface> surface);
private:
ScopedObject<Context> context_;
std::shared_ptr<impeller::Surface> surface_;

View File

@ -15,6 +15,7 @@ Texture::Texture(const Context& context, const TextureDescriptor& descriptor) {
if (!texture || !texture->IsValid()) {
return;
}
texture->SetLabel("UserCreated");
backend_ = context.GetContext()->GetBackendType();
texture_ = std::move(texture);
}