From 41f1e920d8fda0dadc944d3aefb3a0b8750da23e Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Fri, 15 Aug 2025 09:25:53 -0400 Subject: [PATCH] LibGfx: Allow creating a PaintingSurface from a Vulkan image This adds a new PaintingSurface creation function, create_from_vkimage, which returns a PaintingSurface backed by a vulkan image. It's analogous to the existing create_from_iosurface function. In both cases the backing object will be imported into Skia as a render target and then an SkSurface will be wrapped around that. In order to ensure that the image will not be freed while still in use by Skia, we will manually bump the refcount of the VulkanImage object before passing it to Skia and then use the releaseCallback parameter of WrapBackendRenderTarget to register a callback that drops this reference. --- Libraries/LibGfx/PaintingSurface.cpp | 79 +++++++++++++++++++++++----- Libraries/LibGfx/PaintingSurface.h | 4 ++ 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/Libraries/LibGfx/PaintingSurface.cpp b/Libraries/LibGfx/PaintingSurface.cpp index f645dbeaa03..bf1528da481 100644 --- a/Libraries/LibGfx/PaintingSurface.cpp +++ b/Libraries/LibGfx/PaintingSurface.cpp @@ -17,6 +17,10 @@ #ifdef AK_OS_MACOS # include #endif +#ifdef USE_VULKAN +# include +# include +#endif namespace Gfx { @@ -27,6 +31,68 @@ struct PaintingSurface::Impl { RefPtr bitmap; }; +static GrSurfaceOrigin origin_to_sk_origin(PaintingSurface::Origin origin) +{ + switch (origin) { + case PaintingSurface::Origin::BottomLeft: + return kBottomLeft_GrSurfaceOrigin; + default: + VERIFY_NOT_REACHED(); + case PaintingSurface::Origin::TopLeft: + return kTopLeft_GrSurfaceOrigin; + } +} + +#ifdef USE_VULKAN +static SkColorType vk_format_to_sk_color_type(VkFormat format) +{ + switch (format) { + case VK_FORMAT_B8G8R8A8_UNORM: + return kBGRA_8888_SkColorType; + // add more as needed + default: + VERIFY_NOT_REACHED(); + return kUnknown_SkColorType; + } +} + +static void release_vulkan_image(void* context) +{ + VulkanImage* image = static_cast(context); + image->unref(); +} + +NonnullRefPtr PaintingSurface::create_from_vkimage(NonnullRefPtr context, NonnullRefPtr vulkan_image, Origin origin) +{ + context->lock(); + ScopeGuard unlock_guard([&context] { + context->unlock(); + }); + + IntSize size(vulkan_image->info.extent.width, vulkan_image->info.extent.height); + GrVkImageInfo info = { + .fImage = vulkan_image->image, + .fAlloc = {}, // we're managing the memory ourselves + .fImageTiling = vulkan_image->info.tiling, + .fImageLayout = vulkan_image->info.layout, + .fFormat = vulkan_image->info.format, + .fImageUsageFlags = vulkan_image->info.usage, + .fSampleCount = 1, + .fLevelCount = 1, + .fCurrentQueueFamily = VK_QUEUE_FAMILY_IGNORED, + .fProtected = skgpu::Protected::kNo, + .fYcbcrConversionInfo = {}, + .fSharingMode = vulkan_image->info.sharing_mode, + }; + GrBackendRenderTarget rt = GrBackendRenderTargets::MakeVk(size.width(), size.height(), info); + // Note, we're implicitly giving Skia a reference to vulkan_image. It will eventually be released by the callback function. + vulkan_image->ref(); + sk_sp surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), rt, origin_to_sk_origin(origin), vk_format_to_sk_color_type(vulkan_image->info.format), + nullptr, nullptr, release_vulkan_image, vulkan_image.ptr()); + return adopt_ref(*new PaintingSurface(make(context, size, surface, nullptr))); +} +#endif + NonnullRefPtr PaintingSurface::create_with_size(RefPtr context, IntSize size, BitmapFormat color_type, AlphaType alpha_type) { auto sk_color_type = to_skia_color_type(color_type); @@ -71,18 +137,7 @@ NonnullRefPtr PaintingSurface::create_from_iosurface(Core::IOSu GrMtlTextureInfo mtl_info; mtl_info.fTexture = sk_ret_cfp(metal_texture->texture()); auto backend_render_target = GrBackendRenderTargets::MakeMtl(metal_texture->width(), metal_texture->height(), mtl_info); - GrSurfaceOrigin sk_origin; - switch (origin) { - case Origin::TopLeft: - sk_origin = kTopLeft_GrSurfaceOrigin; - break; - case Origin::BottomLeft: - sk_origin = kBottomLeft_GrSurfaceOrigin; - break; - default: - VERIFY_NOT_REACHED(); - } - auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, sk_origin, kBGRA_8888_SkColorType, nullptr, nullptr); + auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, origin_to_sk_origin(origin), kBGRA_8888_SkColorType, nullptr, nullptr); return adopt_ref(*new PaintingSurface(make(context, size, surface, nullptr))); } #endif diff --git a/Libraries/LibGfx/PaintingSurface.h b/Libraries/LibGfx/PaintingSurface.h index a2890d74694..db22cd92a5c 100644 --- a/Libraries/LibGfx/PaintingSurface.h +++ b/Libraries/LibGfx/PaintingSurface.h @@ -39,6 +39,10 @@ public: static NonnullRefPtr create_from_iosurface(Core::IOSurfaceHandle&&, NonnullRefPtr, Origin = Origin::TopLeft); #endif +#ifdef USE_VULKAN + static NonnullRefPtr create_from_vkimage(NonnullRefPtr context, NonnullRefPtr vulkan_image, Origin origin); +#endif + void read_into_bitmap(Bitmap&); void write_from_bitmap(Bitmap const&);