Add Graphite variants of *VkRenderEngine, *GpuContext, & *BackendTexture

GraphiteVkRenderEngine is currently unused, but I've tested it locally
by modifying SkiaVkRenderEngine::create

Test: manual validation (unused, will add to tests soon)
Bug: b/293371537
Change-Id: I961a40e5073bd5452886c486e2ce19b62a7b12a7
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
new file mode 100644
index 0000000..3dd9ed2
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphiteBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <include/core/SkSurfaceProps.h>
+#include <include/gpu/graphite/Image.h>
+#include <include/gpu/graphite/Surface.h>
+#include <include/gpu/graphite/TextureInfo.h>
+
+#include "skia/ColorSpaces.h"
+
+#include <android/hardware_buffer.h>
+#include <inttypes.h>
+#include <log/log_main.h>
+#include <utils/Trace.h>
+
+namespace android::renderengine::skia {
+
+GraphiteBackendTexture::GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
+                                               AHardwareBuffer* buffer, bool isOutputBuffer)
+      : SkiaBackendTexture(buffer, isOutputBuffer), mRecorder(std::move(recorder)) {
+    ATRACE_CALL();
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(buffer, &desc);
+    const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+
+    const SkISize dimensions = {static_cast<int32_t>(desc.width),
+                                static_cast<int32_t>(desc.height)};
+    LOG_ALWAYS_FATAL_IF(static_cast<uint32_t>(dimensions.width()) != desc.width ||
+                                static_cast<uint32_t>(dimensions.height()) != desc.height,
+                        "Failed to create a valid texture, casting unsigned dimensions [%" PRIu32
+                        ",%" PRIu32 "] to signed [%" PRIo32 ",%" PRIo32 "] "
+                        "is invalid",
+                        desc.width, desc.height, dimensions.width(), dimensions.height());
+
+    mBackendTexture = mRecorder->createBackendTexture(buffer, isOutputBuffer, createProtectedImage,
+                                                      dimensions, false);
+    if (!mBackendTexture.isValid() || !dimensions.width() || !dimensions.height()) {
+        LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+                         "isWriteable:%d format:%d",
+                         this, dimensions.width(), dimensions.height(), createProtectedImage,
+                         isOutputBuffer, desc.format);
+    }
+}
+
+GraphiteBackendTexture::~GraphiteBackendTexture() {
+    if (mBackendTexture.isValid()) {
+        mRecorder->deleteBackendTexture(mBackendTexture);
+        mBackendTexture = {};
+    }
+}
+
+sk_sp<SkImage> GraphiteBackendTexture::makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+                                                 TextureReleaseProc releaseImageProc,
+                                                 ReleaseContext releaseContext) {
+    const SkColorType colorType = colorTypeForImage(alphaType);
+    sk_sp<SkImage> image =
+            SkImages::WrapTexture(mRecorder.get(), mBackendTexture, colorType, alphaType,
+                                  toSkColorSpace(dataspace), releaseImageProc, releaseContext);
+    if (!image) {
+        logFatalTexture("Unable to generate SkImage.", dataspace, colorType);
+    }
+    return image;
+}
+
+sk_sp<SkSurface> GraphiteBackendTexture::makeSurface(ui::Dataspace dataspace,
+                                                     TextureReleaseProc releaseSurfaceProc,
+                                                     ReleaseContext releaseContext) {
+    const SkColorType colorType = internalColorType();
+    SkSurfaceProps props;
+    sk_sp<SkSurface> surface =
+            SkSurfaces::WrapBackendTexture(mRecorder.get(), mBackendTexture, colorType,
+                                           toSkColorSpace(dataspace), &props, releaseSurfaceProc,
+                                           releaseContext);
+    if (!surface) {
+        logFatalTexture("Unable to generate SkSurface.", dataspace, colorType);
+    }
+    return surface;
+}
+
+void GraphiteBackendTexture::logFatalTexture(const char* msg, ui::Dataspace dataspace,
+                                             SkColorType colorType) {
+    // TODO: b/293371537 - Iterate on this logging (validate failure cases, possibly check
+    // VulkanTextureInfo, etc.)
+    const skgpu::graphite::TextureInfo& textureInfo = mBackendTexture.info();
+    LOG_ALWAYS_FATAL("%s isOutputBuffer:%d, dataspace:%d, colorType:%d"
+                     "\n\tBackendTexture: isValid:%d, dimensions:%dx%d"
+                     "\n\t\tTextureInfo: isValid:%d, numSamples:%d, mipmapped:%d, isProtected: %d",
+                     msg, isOutputBuffer(), static_cast<int32_t>(dataspace), colorType,
+                     mBackendTexture.isValid(), mBackendTexture.dimensions().width(),
+                     mBackendTexture.dimensions().height(), textureInfo.isValid(),
+                     textureInfo.numSamples(), static_cast<int32_t>(textureInfo.mipmapped()),
+                     static_cast<int32_t>(textureInfo.isProtected()));
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.h b/libs/renderengine/skia/compat/GraphiteBackendTexture.h
new file mode 100644
index 0000000..3bec3f7
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SkiaBackendTexture.h"
+
+#include <include/core/SkColorSpace.h>
+#include <include/core/SkImage.h>
+#include <include/core/SkSurface.h>
+#include <include/gpu/graphite/BackendTexture.h>
+#include <include/gpu/graphite/Recorder.h>
+
+#include <android-base/macros.h>
+#include <ui/GraphicTypes.h>
+
+#include <memory>
+
+namespace android::renderengine::skia {
+
+class GraphiteBackendTexture : public SkiaBackendTexture {
+public:
+    // Creates an internal skgpu::graphite::BackendTexture whose contents come from the provided
+    // buffer.
+    GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
+                           AHardwareBuffer* buffer, bool isOutputBuffer);
+
+    ~GraphiteBackendTexture() override;
+
+    sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+                             TextureReleaseProc releaseImageProc,
+                             ReleaseContext releaseContext) override;
+
+    sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace, TextureReleaseProc releaseSurfaceProc,
+                                 ReleaseContext releaseContext) override;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(GraphiteBackendTexture);
+
+    void logFatalTexture(const char* msg, ui::Dataspace dataspace, SkColorType colorType);
+
+    const std::shared_ptr<skgpu::graphite::Recorder> mRecorder;
+    skgpu::graphite::BackendTexture mBackendTexture;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
new file mode 100644
index 0000000..e19d66f
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphiteGpuContext.h"
+
+#include <include/core/SkImageInfo.h>
+#include <include/core/SkSurface.h>
+#include <include/core/SkTraceMemoryDump.h>
+#include <include/gpu/graphite/GraphiteTypes.h>
+#include <include/gpu/graphite/Surface.h>
+#include <include/gpu/graphite/vk/VulkanGraphiteUtils.h>
+
+#include "GpuTypes.h"
+#include "skia/compat/GraphiteBackendTexture.h"
+
+#include <android-base/macros.h>
+#include <log/log_main.h>
+#include <memory>
+
+namespace android::renderengine::skia {
+
+namespace {
+static skgpu::graphite::ContextOptions graphiteOptions() {
+    skgpu::graphite::ContextOptions options;
+    options.fDisableDriverCorrectnessWorkarounds = true;
+    return options;
+}
+} // namespace
+
+std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Graphite(
+        const skgpu::VulkanBackendContext& vulkanBackendContext) {
+    return std::make_unique<GraphiteGpuContext>(
+            skgpu::graphite::ContextFactory::MakeVulkan(vulkanBackendContext, graphiteOptions()));
+}
+
+GraphiteGpuContext::GraphiteGpuContext(std::unique_ptr<skgpu::graphite::Context> context)
+      : mContext(std::move(context)) {
+    LOG_ALWAYS_FATAL_IF(mContext.get() == nullptr, "graphite::Context creation failed");
+    LOG_ALWAYS_FATAL_IF(mContext->backend() != skgpu::BackendApi::kVulkan,
+                        "graphite::Context::backend() == %d, but GraphiteBackendContext makes "
+                        "assumptions that are only valid for Vulkan (%d)",
+                        static_cast<int>(mContext->backend()),
+                        static_cast<int>(skgpu::BackendApi::kVulkan));
+
+    // TODO: b/293371537 - Iterate on default cache limits (the Recorder should have the majority of
+    // the budget, and the Context should be given a smaller fraction.)
+    skgpu::graphite::RecorderOptions recorderOptions = skgpu::graphite::RecorderOptions();
+    this->mRecorder = mContext->makeRecorder(recorderOptions);
+    LOG_ALWAYS_FATAL_IF(mRecorder.get() == nullptr, "graphite::Recorder creation failed");
+}
+
+std::shared_ptr<skgpu::graphite::Context> GraphiteGpuContext::graphiteContext() {
+    return mContext;
+}
+
+std::shared_ptr<skgpu::graphite::Recorder> GraphiteGpuContext::graphiteRecorder() {
+    return mRecorder;
+}
+
+std::unique_ptr<SkiaBackendTexture> GraphiteGpuContext::makeBackendTexture(AHardwareBuffer* buffer,
+                                                                           bool isOutputBuffer) {
+    return std::make_unique<GraphiteBackendTexture>(graphiteRecorder(), buffer, isOutputBuffer);
+}
+
+sk_sp<SkSurface> GraphiteGpuContext::createRenderTarget(SkImageInfo imageInfo) {
+    constexpr SkSurfaceProps* kProps = nullptr;
+    return SkSurfaces::RenderTarget(mRecorder.get(), imageInfo, skgpu::Mipmapped::kNo, kProps);
+}
+
+size_t GraphiteGpuContext::getMaxRenderTargetSize() const {
+    // maxRenderTargetSize only differs from maxTextureSize on GL, so as long as Graphite implies
+    // Vk, then the distinction is irrelevant.
+    return getMaxTextureSize();
+};
+
+size_t GraphiteGpuContext::getMaxTextureSize() const {
+    return mContext->maxTextureSize();
+};
+
+bool GraphiteGpuContext::isAbandonedOrDeviceLost() {
+    return mContext->isDeviceLost();
+}
+
+void GraphiteGpuContext::finishRenderingAndAbandonContext() {
+    // TODO: b/293371537 - Validate that nothing else needs to be explicitly abandoned.
+    mContext->submit(skgpu::graphite::SyncToCpu::kYes);
+};
+
+void GraphiteGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
+    mContext->dumpMemoryStatistics(traceMemoryDump);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.h b/libs/renderengine/skia/compat/GraphiteGpuContext.h
new file mode 100644
index 0000000..685f899
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SkiaGpuContext.h"
+#include "graphite/Recorder.h"
+
+#include <android-base/macros.h>
+
+namespace android::renderengine::skia {
+
+class GraphiteGpuContext : public SkiaGpuContext {
+public:
+    GraphiteGpuContext(std::unique_ptr<skgpu::graphite::Context> context);
+    ~GraphiteGpuContext() override = default;
+
+    std::shared_ptr<skgpu::graphite::Context> graphiteContext() override;
+    std::shared_ptr<skgpu::graphite::Recorder> graphiteRecorder() override;
+
+    std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
+                                                           bool isOutputBuffer) override;
+
+    sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) override;
+
+    size_t getMaxRenderTargetSize() const override;
+    size_t getMaxTextureSize() const override;
+    bool isAbandonedOrDeviceLost() override;
+    // No-op (large resources like textures, surfaces, images, etc. created by clients don't count
+    // towards Graphite's internal caching budgets, so adjusting its limits based on display change
+    // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking
+    // functions yet, as its design may evolve.)
+    void setResourceCacheLimit(size_t maxResourceBytes) override{};
+
+    void finishRenderingAndAbandonContext() override;
+    // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching
+    // contexts.
+    // No-op (unnecessary during context switch for Graphite's client-budgeted memory model).
+    void purgeUnlockedScratchResources() override{};
+    // No-op (only applicable to GL).
+    void resetContextIfApplicable() override{};
+
+    void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(GraphiteGpuContext);
+
+    const std::shared_ptr<skgpu::graphite::Context> mContext;
+    std::shared_ptr<skgpu::graphite::Recorder> mRecorder;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index 8222e62..a2457e5 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -22,7 +22,9 @@
 #include <include/core/SkSurface.h>
 #include <include/gpu/GrDirectContext.h>
 #include <include/gpu/gl/GrGLInterface.h>
+#include <include/gpu/graphite/Context.h>
 #include <include/gpu/vk/GrVkBackendContext.h>
+#include "include/gpu/vk/VulkanBackendContext.h"
 
 #include "SkiaBackendTexture.h"
 
@@ -41,14 +43,16 @@
             sk_sp<const GrGLInterface> glInterface,
             GrContextOptions::PersistentCache& skSLCacheMonitor);
 
-    // TODO: b/293371537 - Graphite variant.
     static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh(
             const GrVkBackendContext& grVkBackendContext,
             GrContextOptions::PersistentCache& skSLCacheMonitor);
 
+    // TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite.
+    static std::unique_ptr<SkiaGpuContext> MakeVulkan_Graphite(
+            const skgpu::VulkanBackendContext& vulkanBackendContext);
+
     virtual ~SkiaGpuContext() = default;
 
-    // TODO: b/293371537 - Maybe expose whether this SkiaGpuContext is using Ganesh or Graphite?
     /**
      * Only callable on Ganesh-backed instances of SkiaGpuContext, otherwise fatal.
      */
@@ -56,6 +60,20 @@
         LOG_ALWAYS_FATAL("grDirectContext() called on a non-Ganesh instance of SkiaGpuContext!");
     }
 
+    /**
+     * Only callable on Graphite-backed instances of SkiaGpuContext, otherwise fatal.
+     */
+    virtual std::shared_ptr<skgpu::graphite::Context> graphiteContext() {
+        LOG_ALWAYS_FATAL("graphiteContext() called on a non-Graphite instance of SkiaGpuContext!");
+    }
+
+    /**
+     * Only callable on Graphite-backed instances of SkiaGpuContext, otherwise fatal.
+     */
+    virtual std::shared_ptr<skgpu::graphite::Recorder> graphiteRecorder() {
+        LOG_ALWAYS_FATAL("graphiteRecorder() called on a non-Graphite instance of SkiaGpuContext!");
+    }
+
     virtual std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
                                                                    bool isOutputBuffer) = 0;