Skip to content

Commit

Permalink
[Impeller] libImpeller: Add support for Metal and Vulkan rendering.
Browse files Browse the repository at this point in the history
The tests were already parameterized. This enables the remaining backends.

Fixes flutter/flutter#159512
  • Loading branch information
chinmaygarde committed Dec 10, 2024
1 parent 05fdaa6 commit 327902b
Show file tree
Hide file tree
Showing 45 changed files with 1,520 additions and 174 deletions.
10 changes: 9 additions & 1 deletion impeller/playground/backend/vulkan/playground_impl_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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<void*>(::glfwGetInstanceProcAddress(
reinterpret_cast<VkInstance>(instance), proc_name));
};
}

} // namespace impeller
4 changes: 4 additions & 0 deletions impeller/playground/backend/vulkan/playground_impl_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions impeller/playground/playground.cc
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,9 @@ Playground::GLProcAddressResolver Playground::CreateGLProcAddressResolver()
return impl_->CreateGLProcAddressResolver();
}

Playground::VKProcAddressResolver Playground::CreateVKProcAddressResolver()
const {
return impl_->CreateVKProcAddressResolver();
}

} // namespace impeller
4 changes: 4 additions & 0 deletions impeller/playground/playground.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;

Expand Down
5 changes: 5 additions & 0 deletions impeller/playground/playground_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,9 @@ Playground::GLProcAddressResolver PlaygroundImpl::CreateGLProcAddressResolver()
return nullptr;
}

Playground::VKProcAddressResolver PlaygroundImpl::CreateVKProcAddressResolver()
const {
return nullptr;
}

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/playground/playground_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class PlaygroundImpl {

virtual Playground::GLProcAddressResolver CreateGLProcAddressResolver() const;

virtual Playground::VKProcAddressResolver CreateVKProcAddressResolver() const;

protected:
const PlaygroundSwitches switches_;

Expand Down
3 changes: 2 additions & 1 deletion impeller/renderer/backend/vulkan/context_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,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_;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class KHRSwapchainImplVK final

const ISize& GetSize() const;

std::optional<ISize> GetCurrentUnderlyingSurfaceSize() const;

private:
std::weak_ptr<Context> context_;
vk::UniqueSurfaceKHR surface_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ void KHRSwapchainVK::UpdateSurfaceSize(const ISize& size) {
}

std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable() {
return AcquireNextDrawable(0u);
}

std::unique_ptr<Surface> KHRSwapchainVK::AcquireNextDrawable(
size_t resize_retry_count) {
if (!IsValid()) {
return nullptr;
}
Expand All @@ -51,8 +56,19 @@ std::unique_ptr<Surface> 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();
Expand All @@ -76,7 +92,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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,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
Expand Down
93 changes: 83 additions & 10 deletions impeller/toolkit/interop/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -59,6 +68,8 @@ impeller_component("interop") {
]

public_deps = [
":embedded_icu_data",
":interop_api",
"../../base",
"../../display_list",
"../../entity",
Expand All @@ -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") {
Expand All @@ -79,19 +97,62 @@ 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",
"//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

Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions impeller/toolkit/interop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions impeller/toolkit/interop/backend/BUILD.gn
Original file line number Diff line number Diff line change
@@ -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" ]
}
}
17 changes: 17 additions & 0 deletions impeller/toolkit/interop/backend/gles/BUILD.gn
Original file line number Diff line number Diff line change
@@ -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",
]
}
Loading

0 comments on commit 327902b

Please sign in to comment.