[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