[piet] Add piet-gpu support code
- Added a third-party DEPS entry for piet-gpu.
- Added a build script and instructions to compile the
piet-gpu/pgpu-render Rust crate manually.
- Added GN targets to pull in library include and dylib dependencies,
conditioned on a new `skia_use_piet` GN arg.
- Added C++ support code for the piet library under src/gpu/piet
- Made piet a conditional dependency of graphite.
Bug: b/241580255
Change-Id: I666195e93bb1f43cf07d918a23199075b479f1a1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/566017
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Arman Uguray <armansito@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 71d19dd..147e0ef 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1043,6 +1043,13 @@
}
}
+optional("piet") {
+ enabled = skia_use_piet
+ public_defines = [ "SK_ENABLE_PIET_GPU" ]
+ public_deps = [ "//third_party/piet-gpu" ]
+ sources = skia_piet_gpu_sources
+}
+
optional("gif") {
enabled = !skia_use_wuffs && skia_use_libgifcodec
_libgifcodec_gni_path = "third_party/externals/libgifcodec/libgifcodec.gni"
@@ -1135,7 +1142,10 @@
frameworks = []
enabled = skia_enable_graphite
- deps = [ ":gpu_shared" ]
+ deps = [
+ ":gpu_shared",
+ ":piet",
+ ]
public_defines = [ "SK_GRAPHITE_ENABLED" ]
public = skia_graphite_public
sources = skia_graphite_sources
@@ -1333,6 +1343,7 @@
":jpegxl_decode",
":ndk_images",
":none",
+ ":piet",
":png_decode",
":png_encode",
":raw",
diff --git a/DEPS b/DEPS
index 826c8ed..1feb35b 100644
--- a/DEPS
+++ b/DEPS
@@ -39,6 +39,7 @@
"third_party/externals/oboe" : "https://chromium.googlesource.com/external/github.com/google/oboe.git@b02a12d1dd821118763debec6b83d00a8a0ee419",
"third_party/externals/opengl-registry" : "https://skia.googlesource.com/external/github.com/KhronosGroup/OpenGL-Registry@14b80ebeab022b2c78f84a573f01028c96075553",
"third_party/externals/perfetto" : "https://android.googlesource.com/platform/external/perfetto@93885509be1c9240bc55fa515ceb34811e54a394",
+ "third_party/externals/piet-gpu" : "https://skia.googlesource.com/external/github.com/linebender/piet-gpu.git@947a85f9cfb97f946b9bbd32ca9c2c1e5246b482",
"third_party/externals/piex" : "https://android.googlesource.com/platform/external/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
"third_party/externals/rive" : "https://skia.googlesource.com/external/github.com/rive-app/rive-cpp.git@c9ff7391efb75e81c3670ddca77a5893538a26fa",
"third_party/externals/sfntly" : "https://chromium.googlesource.com/external/github.com/googlei18n/sfntly.git@b55ff303ea2f9e26702b514cf6a3196a2e3e2974",
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 9639e2c..eee425e 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -835,3 +835,11 @@
]
skia_shared_vk_sources = []
+
+skia_piet_gpu_sources = [
+ "$_src/gpu/piet/PietTypes.h",
+ "$_src/gpu/piet/Render.cpp",
+ "$_src/gpu/piet/Render.h",
+ "$_src/gpu/piet/Scene.cpp",
+ "$_src/gpu/piet/Scene.h",
+]
diff --git a/gn/skia.gni b/gn/skia.gni
index 2551dee..6c604da 100644
--- a/gn/skia.gni
+++ b/gn/skia.gni
@@ -65,6 +65,7 @@
skia_use_metal = false
skia_use_ndk_images = is_android && defined(ndk_api) && ndk_api >= 30
skia_use_perfetto = is_linux || is_mac || is_android
+ skia_use_piet = false
skia_use_piex = !is_win && !is_wasm
skia_use_sfml = false
skia_use_webgl = is_wasm
diff --git a/src/gpu/piet/PietTypes.h b/src/gpu/piet/PietTypes.h
new file mode 100644
index 0000000..f78e13a
--- /dev/null
+++ b/src/gpu/piet/PietTypes.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_piet_PietTypes_DEFINED
+#define skgpu_piet_PietTypes_DEFINED
+
+#include <pgpu.h>
+
+#include <memory>
+#include <optional>
+
+namespace skgpu::piet {
+
+using Transform = PgpuTransform;
+
+inline Transform identity_transform() { return {1, 0, 0, 1, 0, 0}; }
+
+/**
+ * Wrapper around a pgpu-render type that automatically frees resources on destruction by calling
+ * the appropriate pgpu-render API function.
+ */
+template <typename Type, void (*DtorFunc)(Type*)> class Object {
+public:
+ explicit Object(Type* handle) : fHandle(handle) {}
+ Object() : fHandle(nullptr) {}
+
+ Object(Object&&) = default;
+ Object& operator=(Object&&) = default;
+
+ virtual ~Object() = default;
+
+ Type* get() const { return fHandle.get(); }
+
+ operator bool() const { return this->get() != nullptr; }
+
+protected:
+ void reset() { fHandle = nullptr; }
+
+private:
+ Object(const Object&) = delete;
+ Object& operator=(const Object&) = delete;
+
+ struct Deleter {
+ void operator()(Type* t) {
+ if (t) {
+ DtorFunc(t);
+ }
+ }
+ };
+ std::unique_ptr<Type, Deleter> fHandle;
+};
+
+} // namespace skgpu::piet
+
+#endif // skgpu_piet_PietTypes_DEFINED
diff --git a/src/gpu/piet/README.md b/src/gpu/piet/README.md
new file mode 100644
index 0000000..997025d
--- /dev/null
+++ b/src/gpu/piet/README.md
@@ -0,0 +1,26 @@
+piet-gpu Utilities
+==================
+
+This directory provides utilities to ease integrating Skia C++ code with the piet-gpu/pgpu-render
+Rust crate's C FFI bindings.
+
+### Building the piet-gpu library
+
+The code depends on a third-party Rust library which must be compiled manually:
+
+1. First make sure that a recent version of `cargo` is installed on your system. Simply follow the
+ instructions on https://doc.rust-lang.org/cargo/getting-started/installation.html to get started.
+
+2. Use the Makefile under `//third_party/piet-gpu` to compile the library:
+```
+$ cd $SKIA_ROOT/third_party/piet-gpu
+$ make debug
+```
+This should create `$SKIA_ROOT/third_party/piet-gpu/out/debug/libpgpu_render.dylib` if the build is
+successful. For a release build, run `make release` instead.
+
+### Building Skia with piet support
+
+Build rules are currently only provided for the GN build. To enable piet support, add
+`skia_use_piet=true` to your GN args. This will enable both the Skia GN targets and define the
+`SK_ENABLE_PIET_GPU` macro which can be used in C++ code to query for support.
diff --git a/src/gpu/piet/Render.cpp b/src/gpu/piet/Render.cpp
new file mode 100644
index 0000000..b05f806
--- /dev/null
+++ b/src/gpu/piet/Render.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/piet/Render.h"
+
+#include "include/core/SkTypes.h"
+#include "src/gpu/piet/Scene.h"
+
+namespace skgpu::piet {
+
+RendererBase::RendererBase(void* device, void* queue) : Object(pgpu_renderer_new(device, queue)) {
+ SkASSERT(this->get() != nullptr);
+}
+
+void RendererBase::render(const Scene& scene, void* target, void* cmdBuffer) const {
+ // TODO: track ID and release resources upon completion
+ pgpu_renderer_render(this->get(), scene.get(), target, cmdBuffer);
+}
+
+} // namespace skgpu::piet
diff --git a/src/gpu/piet/Render.h b/src/gpu/piet/Render.h
new file mode 100644
index 0000000..ba10415
--- /dev/null
+++ b/src/gpu/piet/Render.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_piet_Renderer_DEFINED
+#define skgpu_piet_Renderer_DEFINED
+
+#include "src/gpu/piet/PietTypes.h"
+
+#include <memory>
+
+#if defined(SK_METAL) && defined(__OBJC__)
+#import <Metal/Metal.h>
+#endif
+
+/**
+ * Renderer provides the piet library interface for encoding compute dispatches to render a
+ * skgpu::piet::Scene to a target texture. The Renderer template is meant to be specialized to
+ * directly depend on graphics backend types and acts as the glue between a GPU backend and the
+ * pgpu-render library.
+ *
+ * For instance, the `MtlRenderer` specialization operates directly on Metal framework types and
+ * this header file can be included directly in an Obj-C++ program.
+ */
+
+namespace skgpu::piet {
+
+class Scene;
+
+class RendererBase : public Object<PgpuRenderer, pgpu_renderer_destroy> {
+protected:
+ RendererBase(void* device, void* queue);
+ ~RendererBase() override = default;
+
+ void render(const Scene& scene, void* target, void* cmdBuffer) const;
+
+private:
+ RendererBase(const RendererBase&) = delete;
+ RendererBase(RendererBase&&) = delete;
+};
+
+template <typename BackendTraits> class Renderer final : public RendererBase {
+ using Device = typename BackendTraits::Device;
+ using CommandQueue = typename BackendTraits::CommandQueue;
+ using CommandBuffer = typename BackendTraits::CommandBuffer;
+ using Texture = typename BackendTraits::Texture;
+
+public:
+ Renderer(Device device, CommandQueue queue)
+ : RendererBase(static_cast<void*>(device), static_cast<void*>(queue)) {}
+
+ ~Renderer() override = default;
+
+ void render(const Scene& scene, Texture target, CommandBuffer cmdBuffer) const {
+ RendererBase::render(scene, static_cast<void*>(target), static_cast<void*>(cmdBuffer));
+ }
+};
+
+#if defined(SK_METAL) && defined(__OBJC__)
+
+// The MtlRenderer is inteded to be accessed by Obj-C code as it has a direct dependency on Metal
+// framework objects.
+struct MtlBackendTraits {
+ using Device = id<MTLDevice>;
+ using CommandQueue = id<MTLCommandQueue>;
+ using CommandBuffer = id<MTLCommandBuffer>;
+ using Texture = id<MTLTexture>;
+};
+using MtlRenderer = Renderer<MtlBackendTraits>;
+
+#endif // defined(SK_METAL) && defined(__OBJC__)
+
+} // namespace skgpu::piet
+
+#endif // skgpu_piet_Renderer_DEFINED
diff --git a/src/gpu/piet/Scene.cpp b/src/gpu/piet/Scene.cpp
new file mode 100644
index 0000000..3afaffd
--- /dev/null
+++ b/src/gpu/piet/Scene.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/piet/Scene.h"
+
+#include "include/core/SkTypes.h"
+
+namespace skgpu::piet {
+namespace {
+
+bool get_piet_verb(SkPath::Verb verb, PgpuPathVerb* outVerb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ *outVerb = PgpuPathVerb::MoveTo;
+ break;
+ case SkPath::kLine_Verb:
+ *outVerb = PgpuPathVerb::LineTo;
+ break;
+ case SkPath::kQuad_Verb:
+ *outVerb = PgpuPathVerb::QuadTo;
+ break;
+ case SkPath::kConic_Verb:
+ case SkPath::kCubic_Verb:
+ *outVerb = PgpuPathVerb::CurveTo;
+ break;
+ case SkPath::kClose_Verb:
+ *outVerb = PgpuPathVerb::Close;
+ break;
+ case SkPath::kDone_Verb:
+ default:
+ return false;
+ }
+ return true;
+}
+
+PgpuFill get_fill_type(SkPathFillType fillType) {
+ switch (fillType) {
+ case SkPathFillType::kWinding:
+ return PgpuFill::NonZero;
+ case SkPathFillType::kEvenOdd:
+ return PgpuFill::EvenOdd;
+ default:
+ // TODO(b/238756757): pgpu-render doesn't define fill types for kInverseWinding and
+ // kInverseEvenOdd. This should be updated to support those cases.
+ SkDebugf("piet: unsupported fill type\n");
+ break;
+ }
+ return PgpuFill::NonZero;
+}
+
+} // namespace
+
+Scene::Scene() : Object(pgpu_scene_new()) { SkASSERT(this->get() != nullptr); }
+
+void Scene::addPath(const SkPath& path, const Transform& transform) {
+ this->initBuilderIfNecessary();
+
+ SkPath::Iter pathIter(path, /*forceClose=*/false);
+ PgpuPathIter iter;
+ iter.context = &pathIter;
+ iter.next_element = [](void* context, PgpuPathElement* outElem) {
+ SkASSERT(outElem);
+ SkASSERT(context);
+
+ SkPoint points[4];
+ SkPath::Verb verb = static_cast<SkPath::Iter*>(context)->next(points);
+ if (!get_piet_verb(verb, &outElem->verb)) {
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ const SkPoint& p = points[i];
+ outElem->points[i] = {p.x(), p.y()};
+ }
+
+ return true;
+ };
+
+ pgpu_scene_builder_transform(fActiveBuilder->get(), &transform);
+ pgpu_scene_builder_fill_path(
+ fActiveBuilder->get(), get_fill_type(path.getFillType()), nullptr, nullptr, &iter);
+}
+
+bool Scene::finalize() {
+ if (!fActiveBuilder) {
+ return false;
+ }
+ fActiveBuilder.reset();
+ return true;
+}
+
+void Scene::initBuilderIfNecessary() {
+ if (!fActiveBuilder) {
+ fActiveBuilder = Builder(pgpu_scene_builder_for_scene(this->get()));
+ SkASSERT(fActiveBuilder);
+ }
+}
+
+} // namespace skgpu::piet
diff --git a/src/gpu/piet/Scene.h b/src/gpu/piet/Scene.h
new file mode 100644
index 0000000..0acb33d
--- /dev/null
+++ b/src/gpu/piet/Scene.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkPath.h"
+#include "include/core/SkRefCnt.h"
+#include "src/gpu/piet/PietTypes.h"
+
+#ifndef skgpu_piet_Scene_DEFINED
+#define skgpu_piet_Scene_DEFINED
+
+namespace skgpu::piet {
+
+class Scene final : public SkRefCnt, public Object<PgpuScene, pgpu_scene_destroy> {
+public:
+ static sk_sp<Scene> Make() { return sk_sp<Scene>(new Scene()); }
+
+ // Insert a single path with the given transform into the scene. This call can be called
+ // multiple times to populate a scene. The changes will take effect following a successful call
+ // to `finalize()`.
+ //
+ // TODO(b/241580303): This mode of usage requires the entire scene to be rebuilt before (for
+ // even incremental changes) before recording. We should provide an interface for populating the
+ // scene via scene fragments instead.
+ void addPath(const SkPath& path, const Transform& transform);
+
+ // Returns true if there were any pending changes to the scene that were finalized and the scene
+ // is now ready to render.
+ //
+ // Any subsequent calls to modify the scene will clear the scene contents first.
+ bool finalize();
+
+private:
+ Scene();
+
+ class Builder final : public Object<PgpuSceneBuilder, pgpu_scene_builder_finish> {
+ public:
+ explicit Builder(PgpuSceneBuilder* builder) : Object(builder) {}
+ };
+
+ void initBuilderIfNecessary();
+
+ std::optional<Builder> fActiveBuilder;
+};
+} // namespace skgpu::piet
+
+#endif // skgpu_piet_Scene_DEFINED
diff --git a/third_party/piet-gpu/BUILD.gn b/third_party/piet-gpu/BUILD.gn
new file mode 100644
index 0000000..2b5c416
--- /dev/null
+++ b/third_party/piet-gpu/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2022 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../third_party.gni")
+
+third_party("headers") {
+ public_include_dirs = [ "../externals/piet-gpu/pgpu-render" ]
+}
+
+config("library_config") {
+ # TODO(b/241583094): This dylib dependency will only work on macOS. This is OK since we support
+ # piet only with Metal for now but we should find a better approach for building and linking
+ # against pgpu-render.
+ if (is_debug) {
+ libs = [ "./out/debug/libpgpu_render.dylib" ]
+ } else {
+ libs = [ "./out/release/libpgpu_render.dylib" ]
+ }
+}
+
+group("piet-gpu") {
+ public_configs = [ ":library_config" ]
+ public_deps = [ ":headers" ]
+}
diff --git a/third_party/piet-gpu/Makefile b/third_party/piet-gpu/Makefile
new file mode 100644
index 0000000..2cac1bc
--- /dev/null
+++ b/third_party/piet-gpu/Makefile
@@ -0,0 +1,9 @@
+debug:
+ cd ../externals/piet-gpu/pgpu-render && \
+ cargo build --target-dir ../../../piet-gpu/out && \
+ cd ../../../piet-gpu
+
+release:
+ cd ../externals/piet-gpu/pgpu-render && \
+ cargo build --release --target-dir ../../../piet-gpu/out && \
+ cd ../../../piet-gpu