diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index 1de431bd41365..7bf7474447818 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -91,7 +91,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()) { @@ -233,4 +232,13 @@ bool PlaygroundImplVK::IsVulkanDriverPresent() { return false; } +// |PlaygroundImpl| +Playground::VKProcAddressResolver +PlaygroundImplVK::CreateVKProcAddressResolver() const { + return [](void* instance, const char* proc_name) -> void* { + return reinterpret_cast(::glfwGetInstanceProcAddress( + reinterpret_cast(instance), proc_name)); + }; +} + } // namespace impeller diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.h b/impeller/playground/backend/vulkan/playground_impl_vk.h index 774030df1ed74..c110fb3c11022 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.h +++ b/impeller/playground/backend/vulkan/playground_impl_vk.h @@ -44,6 +44,10 @@ class PlaygroundImplVK final : public PlaygroundImpl { std::unique_ptr AcquireSurfaceFrame( std::shared_ptr context) override; + // |PlaygroundImpl| + Playground::VKProcAddressResolver CreateVKProcAddressResolver() + const override; + PlaygroundImplVK(const PlaygroundImplVK&) = delete; PlaygroundImplVK& operator=(const PlaygroundImplVK&) = delete; diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index f588a54049480..8952ba7cb6c0c 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -527,4 +527,9 @@ Playground::GLProcAddressResolver Playground::CreateGLProcAddressResolver() return impl_->CreateGLProcAddressResolver(); } +Playground::VKProcAddressResolver Playground::CreateVKProcAddressResolver() + const { + return impl_->CreateVKProcAddressResolver(); +} + } // namespace impeller diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 0481211fd1d36..236e6de89c649 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -118,6 +118,10 @@ class Playground { using GLProcAddressResolver = std::function; GLProcAddressResolver CreateGLProcAddressResolver() const; + using VKProcAddressResolver = + std::function; + VKProcAddressResolver CreateVKProcAddressResolver() const; + protected: const PlaygroundSwitches switches_; diff --git a/impeller/playground/playground_impl.cc b/impeller/playground/playground_impl.cc index b9a8ff62813df..df086c9100400 100644 --- a/impeller/playground/playground_impl.cc +++ b/impeller/playground/playground_impl.cc @@ -71,4 +71,9 @@ Playground::GLProcAddressResolver PlaygroundImpl::CreateGLProcAddressResolver() return nullptr; } +Playground::VKProcAddressResolver PlaygroundImpl::CreateVKProcAddressResolver() + const { + return nullptr; +} + } // namespace impeller diff --git a/impeller/playground/playground_impl.h b/impeller/playground/playground_impl.h index 35e39881c68b6..387a2cb5af44e 100644 --- a/impeller/playground/playground_impl.h +++ b/impeller/playground/playground_impl.h @@ -38,6 +38,8 @@ class PlaygroundImpl { virtual Playground::GLProcAddressResolver CreateGLProcAddressResolver() const; + virtual Playground::VKProcAddressResolver CreateVKProcAddressResolver() const; + protected: const PlaygroundSwitches switches_; diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index d793b3e9c61fd..45f1d8a063d19 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -141,6 +141,7 @@ void ContextVK::Setup(Settings settings) { TRACE_EVENT0("impeller", "ContextVK::Setup"); if (!settings.proc_address_callback) { + VALIDATION_LOG << "Missing proc address callback."; return; } @@ -152,7 +153,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 }); diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc index aeebeb38e8e57..37d5cb6f84fa9 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc @@ -276,6 +276,38 @@ const ISize& KHRSwapchainImplVK::GetSize() const { return size_; } +std::optional 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_; } diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h index 049ab11426266..c9fc7d41ca038 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h @@ -63,6 +63,8 @@ class KHRSwapchainImplVK final const ISize& GetSize() const; + std::optional GetCurrentUnderlyingSurfaceSize() const; + private: std::weak_ptr context_; vk::UniqueSurfaceKHR surface_; diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc index 0877e5541e89a..2a8af4c72e43b 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.cc @@ -40,6 +40,11 @@ void KHRSwapchainVK::UpdateSurfaceSize(const ISize& size) { } std::unique_ptr KHRSwapchainVK::AcquireNextDrawable() { + return AcquireNextDrawable(0u); +} + +std::unique_ptr KHRSwapchainVK::AcquireNextDrawable( + size_t resize_retry_count) { if (!IsValid()) { return nullptr; } @@ -51,8 +56,19 @@ std::unique_ptr KHRSwapchainVK::AcquireNextDrawable() { return std::move(result.surface); } + 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; + } + TRACE_EVENT0("impeller", "RecreateSwapchain"); + size_ = impl_->GetCurrentUnderlyingSurfaceSize().value_or(size_); + // This swapchain implementation indicates that it is out of date. Tear it // down and make a new one. auto context = impl_->GetContext(); @@ -76,7 +92,7 @@ std::unique_ptr 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 { diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h index 21acdfd47a83a..00c72627d8b65 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h @@ -52,6 +52,8 @@ class KHRSwapchainVK final : public SwapchainVK { KHRSwapchainVK(const KHRSwapchainVK&) = delete; KHRSwapchainVK& operator=(const KHRSwapchainVK&) = delete; + + std::unique_ptr AcquireNextDrawable(size_t resize_retry_count); }; } // namespace impeller diff --git a/impeller/toolkit/interop/BUILD.gn b/impeller/toolkit/interop/BUILD.gn index 500410359dcdb..bee866d9315b2 100644 --- a/impeller/toolkit/interop/BUILD.gn +++ b/impeller/toolkit/interop/BUILD.gn @@ -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,40 @@ impeller_component("library") { deps = [ ":interop" ] } -impeller_component("example") { +impeller_component("example_gl") { + target_type = "executable" + + output_name = "impeller_interop_example_gl" + + sources = [ "example_gl.c" ] + + deps = [ + ":interop", + "//flutter/third_party/glfw", + ] +} + +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" + output_name = "impeller_interop_example_vk" - sources = [ "example.c" ] + sources = [ "example_vk.c" ] deps = [ ":interop", @@ -92,6 +138,21 @@ impeller_component("example") { ] } +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) { diff --git a/impeller/toolkit/interop/README.md b/impeller/toolkit/interop/README.md index ac36b128abc0c..bf62b16276e1f 100644 --- a/impeller/toolkit/interop/README.md +++ b/impeller/toolkit/interop/README.md @@ -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 diff --git a/impeller/toolkit/interop/backend/BUILD.gn b/impeller/toolkit/interop/backend/BUILD.gn new file mode 100644 index 0000000000000..2ce918dc07dd0 --- /dev/null +++ b/impeller/toolkit/interop/backend/BUILD.gn @@ -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" ] + } +} diff --git a/impeller/toolkit/interop/backend/gles/BUILD.gn b/impeller/toolkit/interop/backend/gles/BUILD.gn new file mode 100644 index 0000000000000..236b129ca8da2 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/BUILD.gn @@ -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", + ] +} diff --git a/impeller/toolkit/interop/backend/gles/context_gles.cc b/impeller/toolkit/interop/backend/gles/context_gles.cc new file mode 100644 index 0000000000000..1199410a0b320 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/context_gles.cc @@ -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 ContextGLES::Create( + std::function proc_address_callback) { + auto proc_table = std::make_unique( + 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> shader_mappings = { + std::make_shared( + impeller_entity_shaders_gles_data, + impeller_entity_shaders_gles_length), + std::make_shared( + 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(); + 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 ContextGLES::Create( + std::shared_ptr impeller_context, + std::shared_ptr worker) { + // Can't call Create because of private constructor. Adopt the raw pointer + // instead. + auto context = Adopt( + 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 context, + std::shared_ptr worker) + : Context(std::move(context)), worker_(std::move(worker)) {} + +ContextGLES::~ContextGLES() = default; + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/gles/context_gles.h b/impeller/toolkit/interop/backend/gles/context_gles.h new file mode 100644 index 0000000000000..baaf08cf866a5 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/context_gles.h @@ -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 +#include + +#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 Create( + std::function proc_address_callback); + + static ScopedObject Create( + std::shared_ptr context, + std::shared_ptr worker = nullptr); + + ContextGLES(); + + // |Context| + ~ContextGLES() override; + + ContextGLES(const ContextGLES&) = delete; + + ContextGLES& operator=(const ContextGLES&) = delete; + + private: + std::shared_ptr worker_; + + ContextGLES(std::shared_ptr context, + std::shared_ptr worker); +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_GLES_CONTEXT_GLES_H_ diff --git a/impeller/toolkit/interop/backend/gles/reactor_worker_gles.cc b/impeller/toolkit/interop/backend/gles/reactor_worker_gles.cc new file mode 100644 index 0000000000000..d4c7bba3a5ed9 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/reactor_worker_gles.cc @@ -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 diff --git a/impeller/toolkit/interop/backend/gles/reactor_worker_gles.h b/impeller/toolkit/interop/backend/gles/reactor_worker_gles.h new file mode 100644 index 0000000000000..676c0a2b00457 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/reactor_worker_gles.h @@ -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_ diff --git a/impeller/toolkit/interop/backend/gles/surface_gles.cc b/impeller/toolkit/interop/backend/gles/surface_gles.cc new file mode 100644 index 0000000000000..6860b16115312 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/surface_gles.cc @@ -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 surface) + : Surface(context, std::move(surface)) {} + +SurfaceGLES::~SurfaceGLES() = default; + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/gles/surface_gles.h b/impeller/toolkit/interop/backend/gles/surface_gles.h new file mode 100644 index 0000000000000..a4ca3b3193526 --- /dev/null +++ b/impeller/toolkit/interop/backend/gles/surface_gles.h @@ -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_VULKAN_SURFACE_GLES_H_ +#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_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 surface); + + // |Surface| + ~SurfaceGLES(); + + SurfaceGLES(const SurfaceGLES&) = delete; + + SurfaceGLES& operator=(const SurfaceGLES&) = delete; +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_GLES_H_ diff --git a/impeller/toolkit/interop/backend/metal/BUILD.gn b/impeller/toolkit/interop/backend/metal/BUILD.gn new file mode 100644 index 0000000000000..55f443c974b8c --- /dev/null +++ b/impeller/toolkit/interop/backend/metal/BUILD.gn @@ -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", + ] +} diff --git a/impeller/toolkit/interop/backend/metal/context_mtl.h b/impeller/toolkit/interop/backend/metal/context_mtl.h new file mode 100644 index 0000000000000..e5f01e71101be --- /dev/null +++ b/impeller/toolkit/interop/backend/metal/context_mtl.h @@ -0,0 +1,32 @@ +// 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/toolkit/interop/context.h" + +namespace impeller::interop { + +class ContextMTL final : public Context { + public: + static ScopedObject Create(); + + static ScopedObject Create( + std::shared_ptr context); + + // |Context| + ~ContextMTL() override; + + ContextMTL(const ContextMTL&) = delete; + + ContextMTL& operator=(const ContextMTL&) = delete; + + private: + explicit ContextMTL(std::shared_ptr context); +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_METAL_CONTEXT_MTL_H_ diff --git a/impeller/toolkit/interop/backend/metal/context_mtl.mm b/impeller/toolkit/interop/backend/metal/context_mtl.mm new file mode 100644 index 0000000000000..1d5d714aa9d33 --- /dev/null +++ b/impeller/toolkit/interop/backend/metal/context_mtl.mm @@ -0,0 +1,59 @@ +// 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> +CreateShaderLibraryMappings() { + return {std::make_shared( + impeller_entity_shaders_data, impeller_entity_shaders_length), + std::make_shared( + impeller_modern_shaders_data, impeller_modern_shaders_length), + std::make_shared( + impeller_framebuffer_blend_shaders_data, + impeller_framebuffer_blend_shaders_length), + std::make_shared( + impeller_compute_shaders_data, impeller_compute_shaders_length)}; +} + +ScopedObject ContextMTL::Create() { + auto impeller_context = + impeller::ContextMTL::Create(CreateShaderLibraryMappings(), // + std::make_shared(), // + "Impeller" // + ); + if (!impeller_context) { + VALIDATION_LOG << "Could not create Impeller context."; + return {}; + } + return Create(std::move(impeller_context)); +} + +ScopedObject ContextMTL::Create( + std::shared_ptr impeller_context) { + // Can't call Create because of private constructor. Adopt the raw pointer + // instead. + auto context = Adopt(new ContextMTL(std::move(impeller_context))); + if (!context->IsValid()) { + VALIDATION_LOG << " Could not create valid context."; + return {}; + } + return context; +} + +ContextMTL::ContextMTL(std::shared_ptr context) + : Context(std::move(context)) {} + +ContextMTL::~ContextMTL() = default; + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/metal/surface_mtl.h b/impeller/toolkit/interop/backend/metal/surface_mtl.h new file mode 100644 index 0000000000000..deb5239a66431 --- /dev/null +++ b/impeller/toolkit/interop/backend/metal/surface_mtl.h @@ -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_VULKAN_SURFACE_MTL_H_ +#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_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 surface); + + ~SurfaceMTL(); + + SurfaceMTL(const SurfaceMTL&) = delete; + + SurfaceMTL& operator=(const SurfaceMTL&) = delete; +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_MTL_H_ diff --git a/impeller/toolkit/interop/backend/metal/surface_mtl.mm b/impeller/toolkit/interop/backend/metal/surface_mtl.mm new file mode 100644 index 0000000000000..3c5bc89a28749 --- /dev/null +++ b/impeller/toolkit/interop/backend/metal/surface_mtl.mm @@ -0,0 +1,23 @@ +// 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" + +namespace impeller::interop { + +SurfaceMTL::SurfaceMTL(Context& context, void* metal_drawable) + : SurfaceMTL(context, + impeller::SurfaceMTL::MakeFromMetalLayerDrawable( + context.GetContext(), + (__bridge id)metal_drawable)) {} + +SurfaceMTL::SurfaceMTL(Context& context, + std::shared_ptr surface) + : Surface(context, std::move(surface)) {} + +SurfaceMTL::~SurfaceMTL() = default; + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/vulkan/BUILD.gn b/impeller/toolkit/interop/backend/vulkan/BUILD.gn new file mode 100644 index 0000000000000..585df81f1d586 --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/BUILD.gn @@ -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", + ] +} diff --git a/impeller/toolkit/interop/backend/vulkan/context_vk.cc b/impeller/toolkit/interop/backend/vulkan/context_vk.cc new file mode 100644 index 0000000000000..08e680a3d8891 --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/context_vk.cc @@ -0,0 +1,113 @@ +// 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> +CreateShaderLibraryMappings() { + return { + std::make_shared(impeller_entity_shaders_vk_data, + impeller_entity_shaders_vk_length), + std::make_shared(impeller_modern_shaders_vk_data, + impeller_modern_shaders_vk_length), + std::make_shared( + impeller_framebuffer_blend_shaders_vk_data, + impeller_framebuffer_blend_shaders_vk_length), + std::make_shared( + impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length), + }; +} + +ScopedObject 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; + // This bit is complicated by the fact that impeller::ContextVK::Settings take + // a raw function pointer to the callback. + thread_local decltype(settings.instance_proc_address_callback) sCallback; + sCallback = settings.instance_proc_address_callback; + impeller_settings.proc_address_callback = + [](VkInstance instance, const char* proc_name) -> PFN_vkVoidFunction { + if (sCallback) { + return sCallback(instance, proc_name); + } + return nullptr; + }; + auto impeller_context = + impeller::ContextVK::Create(std::move(impeller_settings)); + sCallback = nullptr; + if (!impeller_context) { + VALIDATION_LOG << "Could not create Impeller context."; + return {}; + } + return Create(std::move(impeller_context)); +} + +ScopedObject ContextVK::Create( + std::shared_ptr impeller_context) { + // Can't call Create because of private constructor. Adopt the raw pointer + // instead. + auto context = Adopt(new ContextVK(std::move(impeller_context))); + if (!context->IsValid()) { + VALIDATION_LOG << " Could not create valid context."; + return {}; + } + return context; +} + +ContextVK::ContextVK(std::shared_ptr 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( + 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()); + + info.vk_instance = reinterpret_cast(VkInstance(context.GetInstance())); + info.vk_physical_device = + reinterpret_cast(VkPhysicalDevice(context.GetPhysicalDevice())); + info.vk_logical_device = + reinterpret_cast(VkDevice(context.GetDevice())); + 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 diff --git a/impeller/toolkit/interop/backend/vulkan/context_vk.h b/impeller/toolkit/interop/backend/vulkan/context_vk.h new file mode 100644 index 0000000000000..164cafc54df2d --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/context_vk.h @@ -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 + instance_proc_address_callback; + bool enable_validation = false; + + explicit Settings(const ImpellerContextVulkanSettings& settings); + + bool IsValid() const; + }; + + static ScopedObject Create(const Settings& settings); + + static ScopedObject Create( + std::shared_ptr context); + + // |Context| + ~ContextVK() override; + + ContextVK(const ContextVK&) = delete; + + ContextVK& operator=(const ContextVK&) = delete; + + bool GetInfo(ImpellerContextVulkanInfo& info) const; + + private: + ContextVK(std::shared_ptr context); +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_CONTEXT_VK_H_ diff --git a/impeller/toolkit/interop/backend/vulkan/surface_vk.cc b/impeller/toolkit/interop/backend/vulkan/surface_vk.cc new file mode 100644 index 0000000000000..735e387057465 --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/surface_vk.cc @@ -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 surface) + : Surface(context, std::move(surface)) {} + +SurfaceVK::~SurfaceVK() = default; + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/vulkan/surface_vk.h b/impeller/toolkit/interop/backend/vulkan/surface_vk.h new file mode 100644 index 0000000000000..057e5527e38db --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/surface_vk.h @@ -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 surface); + + ~SurfaceVK(); + + SurfaceVK(const SurfaceVK&) = delete; + + SurfaceVK& operator=(const SurfaceVK&) = delete; +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SURFACE_VK_H_ diff --git a/impeller/toolkit/interop/backend/vulkan/swapchain_vk.cc b/impeller/toolkit/interop/backend/vulkan/swapchain_vk.cc new file mode 100644 index 0000000000000..8e2dd9b496879 --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/swapchain_vk.cc @@ -0,0 +1,67 @@ +// 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, + const ISize& size) + : context_(Ref(&context)) { + if (!context.IsVulkan()) { + VALIDATION_LOG << "Context is not Vulkan."; + return; + } + + if (c_surface == NULL) { + VALIDATION_LOG << "Invalid surface."; + return; + } + + // Creating a unique object from a raw handle requires fetching the owner + // manually. + auto surface = vk::UniqueSurfaceKHR( + c_surface, + impeller::ContextVK::Cast(*context_->GetContext()).GetInstance()); + auto swapchain = impeller::SwapchainVK::Create(context.GetContext(), // + std::move(surface), // + size // + ); + 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 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(*context_, std::move(impeller_surface)); + if (!surface || !surface->IsValid()) { + VALIDATION_LOG << "Could not create valid surface."; + return nullptr; + } + + return surface; +} + +} // namespace impeller::interop diff --git a/impeller/toolkit/interop/backend/vulkan/swapchain_vk.h b/impeller/toolkit/interop/backend/vulkan/swapchain_vk.h new file mode 100644 index 0000000000000..34555c64315ab --- /dev/null +++ b/impeller/toolkit/interop/backend/vulkan/swapchain_vk.h @@ -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 { + public: + SwapchainVK(Context& context, VkSurfaceKHR surface, const ISize& size); + + ~SwapchainVK(); + + bool IsValid() const; + + SwapchainVK(const SwapchainVK&) = delete; + + SwapchainVK& operator=(const SwapchainVK&) = delete; + + ScopedObject AcquireNextSurface(); + + private: + ScopedObject context_; + std::shared_ptr swapchain_; +}; + +} // namespace impeller::interop + +#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_BACKEND_VULKAN_SWAPCHAIN_VK_H_ diff --git a/impeller/toolkit/interop/context.cc b/impeller/toolkit/interop/context.cc index 15e32e25045d1..117d15eb32ac9 100644 --- a/impeller/toolkit/interop/context.cc +++ b/impeller/toolkit/interop/context.cc @@ -4,29 +4,12 @@ #include "impeller/toolkit/interop/context.h" -#include - -#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 context, - std::shared_ptr backend_data) - : context_(std::move(context), TypographerContextSkia::Make()), - backend_data_(std::move(backend_data)) {} +Context::Context(std::shared_ptr context) + : context_(std::move(context), TypographerContextSkia::Make()) {} Context::~Context() = default; @@ -38,74 +21,27 @@ std::shared_ptr 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; +AiksContext& Context::GetAiksContext() { + return context_; +} - // |ReactorGLES::Worker| - bool CanReactorReactOnCurrentThreadNow( - const ReactorGLES& reactor) const override { - return thread_id_ == std::this_thread::get_id(); +bool Context::IsBackend(impeller::Context::BackendType type) const { + if (!IsValid()) { + return false; } + return GetContext()->GetBackendType() == type; +} - private: - std::thread::id thread_id_; - - FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker); -}; - -#endif // IMPELLER_ENABLE_OPENGLES +bool Context::IsGL() const { + return IsBackend(impeller::Context::BackendType::kOpenGLES); +} -ScopedObject Context::CreateOpenGLES( - std::function proc_address_callback) { -#if IMPELLER_ENABLE_OPENGLES - auto proc_table = std::make_unique( - 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> shader_mappings = { - std::make_shared( - impeller_entity_shaders_gles_data, - impeller_entity_shaders_gles_length), - std::make_shared( - 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(); - auto worker_id = impeller_context->AddReactorWorker(reactor_worker); - if (!worker_id.has_value()) { - VALIDATION_LOG << "Could not add reactor worker."; - return {}; - } - auto context = - Create(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 +bool Context::IsMetal() const { + return IsBackend(impeller::Context::BackendType::kMetal); } -AiksContext& Context::GetAiksContext() { - return context_; +bool Context::IsVulkan() const { + return IsBackend(impeller::Context::BackendType::kVulkan); } } // namespace impeller::interop diff --git a/impeller/toolkit/interop/context.h b/impeller/toolkit/interop/context.h index 3002ca69945a1..b41b743b01d51 100644 --- a/impeller/toolkit/interop/context.h +++ b/impeller/toolkit/interop/context.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_CONTEXT_H_ #define FLUTTER_IMPELLER_TOOLKIT_INTEROP_CONTEXT_H_ -#include - #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 { public: - class BackendData; - - static ScopedObject CreateOpenGLES( - std::function proc_address_callback); - - explicit Context(std::shared_ptr context, - std::shared_ptr 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 context); + private: impeller::AiksContext context_; - std::shared_ptr backend_data_; }; } // namespace impeller::interop diff --git a/impeller/toolkit/interop/example.c b/impeller/toolkit/interop/example_gl.c similarity index 90% rename from impeller/toolkit/interop/example.c rename to impeller/toolkit/interop/example_gl.c index f121f182fc267..42c1ac94eed3d 100644 --- a/impeller/toolkit/interop/example.c +++ b/impeller/toolkit/interop/example_gl.c @@ -23,10 +23,18 @@ int main(int argc, char const* argv[]) { [[maybe_unused]] int result = glfwInit(); assert(result == GLFW_TRUE); + if (glfwGetPlatform() == GLFW_PLATFORM_COCOA) { + 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; diff --git a/impeller/toolkit/interop/example_mtl.m b/impeller/toolkit/interop/example_mtl.m new file mode 100644 index 0000000000000..8da040b030628 --- /dev/null +++ b/impeller/toolkit/interop/example_mtl.m @@ -0,0 +1,109 @@ +// 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 +#include + +#define GLFW_INCLUDE_NONE +#include "GLFW/glfw3.h" +#define GLFW_EXPOSE_NATIVE_COCOA +#import "GLFW/glfw3native.h" + +#include "impeller.h" + +#include +#include +#include + +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) { + 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; +} diff --git a/impeller/toolkit/interop/example_vk.c b/impeller/toolkit/interop/example_vk.c new file mode 100644 index 0000000000000..a8d55d5c73ed6 --- /dev/null +++ b/impeller/toolkit/interop/example_vk.c @@ -0,0 +1,124 @@ +// 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 +#include + +#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()) { + 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 = {}; + 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) { + 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) { + 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); + ImpellerISize surface_size = {}; + surface_size.width = framebuffer_width; + surface_size.height = framebuffer_height; + ImpellerVulkanSwapchain swapchain = ImpellerVulkanSwapchainCreateNew( + context, vulkan_surface_khr, &surface_size); + 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; +} diff --git a/impeller/toolkit/interop/impeller.cc b/impeller/toolkit/interop/impeller.cc index 7681efe515f9c..5637b859dd466 100644 --- a/impeller/toolkit/interop/impeller.cc +++ b/impeller/toolkit/interop/impeller.cc @@ -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,56 @@ 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(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, + const ImpellerISize* surface_size) { +#if IMPELLER_ENABLE_VULKAN + return Create( + *GetPeer(context), // + reinterpret_cast(vulkan_surface_khr), // + ToImpellerType(*surface_size) // + ) + .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(cull_rect).Leak(); } @@ -475,7 +589,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 +639,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 +723,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(*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(*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 +770,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, diff --git a/impeller/toolkit/interop/impeller.h b/impeller/toolkit/interop/impeller.h index fe1b3bb98094f..ca0c8d5c2441e 100644 --- a/impeller/toolkit/interop/impeller.h +++ b/impeller/toolkit/interop/impeller.h @@ -80,7 +80,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 +288,8 @@ IMPELLER_DEFINE_HANDLE(ImpellerSurface); /// IMPELLER_DEFINE_HANDLE(ImpellerTexture); +IMPELLER_DEFINE_HANDLE(ImpellerVulkanSwapchain); + //------------------------------------------------------------------------------ // Signatures //------------------------------------------------------------------------------ @@ -311,6 +313,11 @@ typedef void* IMPELLER_NULLABLE (*ImpellerProcAddressCallback)( const char* IMPELLER_NONNULL proc_name, void* IMPELLER_NULLABLE user_data); +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 +569,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 +646,14 @@ ImpellerContextCreateOpenGLESNew( ImpellerProcAddressCallback IMPELLER_NONNULL gl_proc_address_callback, void* IMPELLER_NULLABLE gl_proc_address_callback_user_data); +IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerContext IMPELLER_NULLABLE +ImpellerContextCreateMetalNew(uint32_t version); + +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 +672,45 @@ void ImpellerContextRetain(ImpellerContext IMPELLER_NULLABLE context); IMPELLER_EXPORT void ImpellerContextRelease(ImpellerContext IMPELLER_NULLABLE context); +IMPELLER_EXPORT +bool ImpellerContextGetVulkanInfo( + ImpellerContext IMPELLER_NONNULL context, + ImpellerContextVulkanInfo* IMPELLER_NONNULL out_vulkan_info); + +//------------------------------------------------------------------------------ +// Vulkan Swapchain +//------------------------------------------------------------------------------ + +IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerVulkanSwapchain IMPELLER_NULLABLE +ImpellerVulkanSwapchainCreateNew( + ImpellerContext IMPELLER_NONNULL context, + void* IMPELLER_NONNULL vulkan_surface_khr, + const ImpellerISize* IMPELLER_NONNULL surface_size); + +//------------------------------------------------------------------------------ +/// @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); + +IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerSurface IMPELLER_NULLABLE +ImpellerVulkanSwapchainAcquireNextSurfaceNew( + ImpellerVulkanSwapchain IMPELLER_NONNULL swapchain); + //------------------------------------------------------------------------------ // Surface //------------------------------------------------------------------------------ @@ -662,10 +730,15 @@ 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); + +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 +777,12 @@ void ImpellerSurfaceRelease(ImpellerSurface IMPELLER_NULLABLE surface); /// IMPELLER_EXPORT bool ImpellerSurfaceDrawDisplayList( - ImpellerSurface IMPELLER_NULLABLE surface, + ImpellerSurface IMPELLER_NONNULL surface, ImpellerDisplayList IMPELLER_NONNULL display_list); +IMPELLER_EXPORT +bool ImpellerSurfacePresent(ImpellerSurface IMPELLER_NONNULL surface); + //------------------------------------------------------------------------------ // Path //------------------------------------------------------------------------------ diff --git a/impeller/toolkit/interop/impeller_unittests.cc b/impeller/toolkit/interop/impeller_unittests.cc index 19b64cab4fd39..527fa99342e00 100644 --- a/impeller/toolkit/interop/impeller_unittests.cc +++ b/impeller/toolkit/interop/impeller_unittests.cc @@ -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(); diff --git a/impeller/toolkit/interop/playground_test.cc b/impeller/toolkit/interop/playground_test.cc index 03b100dbe9e6e..23d5c09c5d4c2 100644 --- a/impeller/toolkit/interop/playground_test.cc +++ b/impeller/toolkit/interop/playground_test.cc @@ -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 proc_map; @@ -25,6 +40,10 @@ PlaygroundTest::PlaygroundTest() { }); } +PlaygroundTest::PlaygroundTest() { + SetupImpellerHPPProcTableOnce(); +} + PlaygroundTest::~PlaygroundTest() = default; // |PlaygroundTest| @@ -40,8 +59,8 @@ void PlaygroundTest::TearDown() { ScopedObject PlaygroundTest::CreateContext() const { switch (GetBackend()) { case PlaygroundBackend::kMetal: - FML_CHECK(false) << "Metal not yet implemented."; - return nullptr; + return Adopt( + ImpellerContextCreateMetalNew(ImpellerGetVersion())); case PlaygroundBackend::kOpenGLES: { Playground::GLProcAddressResolver playground_gl_proc_address_callback = CreateGLProcAddressResolver(); @@ -55,32 +74,85 @@ ScopedObject PlaygroundTest::CreateContext() const { &playground_gl_proc_address_callback)); } case PlaygroundBackend::kVulkan: - FML_CHECK(false) << "Vulkan not yet implemented."; - return nullptr; + 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(user_data)->resolver; + if (resolver) { + return resolver(instance, proc_name); + } else { + return nullptr; + } + }; + return Adopt( + ImpellerContextCreateVulkanNew(ImpellerGetVersion(), &settings)); + } + FML_UNREACHABLE(); +} + +static ScopedObject CreateSharedSurface( + PlaygroundBackend backend, + Context& context, + std::shared_ptr shared_surface) { + switch (backend) { + case PlaygroundBackend::kMetal: + return Adopt(new SurfaceMTL(context, std::move(shared_surface))); + case PlaygroundBackend::kOpenGLES: + return Adopt( + new SurfaceGLES(context, std::move(shared_surface))); + case PlaygroundBackend::kVulkan: + return Adopt(new SurfaceVK(context, std::move(shared_surface))); } 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(target); - auto surface = Create(*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 CreateSharedContext( + PlaygroundBackend backend, + std::shared_ptr shared_context) { + switch (backend) { + case PlaygroundBackend::kMetal: + return ContextMTL::Create(std::move(shared_context)); + case PlaygroundBackend::kOpenGLES: + return ContextGLES::Create(std::move(shared_context)); + case PlaygroundBackend::kVulkan: + return ContextVK::Create(std::move(shared_context)); + } + FML_UNREACHABLE(); +} + ScopedObject PlaygroundTest::GetInteropContext() { if (interop_context_) { return interop_context_; } - auto context = Create(GetContext(), nullptr); + + auto context = CreateSharedContext(GetBackend(), GetContext()); if (!context) { return nullptr; } diff --git a/impeller/toolkit/interop/surface.cc b/impeller/toolkit/interop/surface.cc index b5e7f7282439f..56a3ae1441938 100644 --- a/impeller/toolkit/interop/surface.cc +++ b/impeller/toolkit/interop/surface.cc @@ -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 surface) Surface::~Surface() = default; -ScopedObject 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(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 diff --git a/impeller/toolkit/interop/surface.h b/impeller/toolkit/interop/surface.h index f489a5e5b775d..eb3467702b888 100644 --- a/impeller/toolkit/interop/surface.h +++ b/impeller/toolkit/interop/surface.h @@ -15,17 +15,9 @@ namespace impeller::interop { -class Surface final +class Surface : public Object { public: - static ScopedObject WrapFBO(Context& context, - uint64_t fbo, - PixelFormat color_format, - ISize size); - - explicit Surface(Context& context, - std::shared_ptr 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 surface); + private: ScopedObject context_; std::shared_ptr surface_; diff --git a/impeller/toolkit/interop/texture.cc b/impeller/toolkit/interop/texture.cc index ccdb501d2ac3f..c74313133fad9 100644 --- a/impeller/toolkit/interop/texture.cc +++ b/impeller/toolkit/interop/texture.cc @@ -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); }