[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:
parent
c5a144435a
commit
8c843aa9d3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -521,4 +521,9 @@ Playground::GLProcAddressResolver Playground::CreateGLProcAddressResolver()
|
||||
return impl_->CreateGLProcAddressResolver();
|
||||
}
|
||||
|
||||
Playground::VKProcAddressResolver Playground::CreateVKProcAddressResolver()
|
||||
const {
|
||||
return impl_->CreateVKProcAddressResolver();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -71,4 +71,9 @@ Playground::GLProcAddressResolver PlaygroundImpl::CreateGLProcAddressResolver()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Playground::VKProcAddressResolver PlaygroundImpl::CreateVKProcAddressResolver()
|
||||
const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
@ -38,6 +38,8 @@ class PlaygroundImpl {
|
||||
|
||||
virtual Playground::GLProcAddressResolver CreateGLProcAddressResolver() const;
|
||||
|
||||
virtual Playground::VKProcAddressResolver CreateVKProcAddressResolver() const;
|
||||
|
||||
protected:
|
||||
const PlaygroundSwitches switches_;
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
21
engine/src/flutter/impeller/toolkit/interop/backend/BUILD.gn
Normal file
21
engine/src/flutter/impeller/toolkit/interop/backend/BUILD.gn
Normal 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" ]
|
||||
}
|
||||
}
|
@ -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",
|
||||
]
|
||||
}
|
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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",
|
||||
]
|
||||
}
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
@ -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",
|
||||
]
|
||||
}
|
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
110
engine/src/flutter/impeller/toolkit/interop/example_mtl.m
Normal file
110
engine/src/flutter/impeller/toolkit/interop/example_mtl.m
Normal 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;
|
||||
}
|
125
engine/src/flutter/impeller/toolkit/interop/example_vk.c
Normal file
125
engine/src/flutter/impeller/toolkit/interop/example_vk.c
Normal 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;
|
||||
}
|
@ -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,
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user