LibGfx+LibWeb: Introduce PaintingSurface that wraps SkSurface from Skia

Adds a new class in LibGfx that represents GPU-accelerated surface and
will be used for both <canvas> and page rendering (display list player).
This commit is contained in:
Aliaksandr Kalenik 2024-09-25 15:42:15 +02:00 committed by Alexander Kalenik
parent 5c1f6bf8ee
commit 25f264b7e7
Notes: github-actions[bot] 2024-11-07 12:49:32 +00:00
8 changed files with 227 additions and 85 deletions

View file

@ -53,6 +53,7 @@ set(SOURCES
ImageFormats/AVIFLoader.cpp
ImmutableBitmap.cpp
MedianCut.cpp
PaintingSurface.cpp
Palette.cpp
Path.cpp
PathSkia.cpp

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <core/SkColorSpace.h>
#include <core/SkSurface.h>
#include <gpu/GrBackendSurface.h>
#include <gpu/GrDirectContext.h>
#include <gpu/ganesh/SkSurfaceGanesh.h>
#ifdef AK_OS_MACOS
# include <gpu/ganesh/mtl/GrMtlBackendContext.h>
# include <gpu/ganesh/mtl/GrMtlBackendSurface.h>
# include <gpu/ganesh/mtl/GrMtlDirectContext.h>
#endif
namespace Gfx {
struct PaintingSurface::Impl {
IntSize size;
sk_sp<SkSurface> surface;
RefPtr<Bitmap> bitmap;
RefPtr<SkiaBackendContext> context;
};
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
{
switch (format) {
case Gfx::BitmapFormat::Invalid:
return kUnknown_SkColorType;
case Gfx::BitmapFormat::BGRA8888:
case Gfx::BitmapFormat::BGRx8888:
return kBGRA_8888_SkColorType;
case Gfx::BitmapFormat::RGBA8888:
return kRGBA_8888_SkColorType;
default:
return kUnknown_SkColorType;
}
}
NonnullRefPtr<PaintingSurface> PaintingSurface::create_with_size(RefPtr<SkiaBackendContext> context, Gfx::IntSize size, Gfx::BitmapFormat color_type, Gfx::AlphaType alpha_type)
{
auto sk_color_type = to_skia_color_type(color_type);
auto sk_alpha_type = alpha_type == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
auto image_info = SkImageInfo::Make(size.width(), size.height(), sk_color_type, sk_alpha_type);
if (!context) {
auto bitmap = Gfx::Bitmap::create(color_type, alpha_type, size).value();
auto surface = SkSurfaces::WrapPixels(image_info, bitmap->begin(), bitmap->pitch());
VERIFY(surface);
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap, context)));
}
auto surface = SkSurfaces::RenderTarget(context->sk_context(), skgpu::Budgeted::kNo, image_info);
VERIFY(surface);
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr, context)));
}
NonnullRefPtr<PaintingSurface> PaintingSurface::wrap_bitmap(Bitmap& bitmap)
{
auto color_type = to_skia_color_type(bitmap.format());
auto alpha_type = bitmap.alpha_type() == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
auto size = bitmap.size();
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type);
auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch());
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, bitmap, nullptr)));
}
#ifdef AK_OS_MACOS
NonnullRefPtr<PaintingSurface> PaintingSurface::wrap_metal_surface(Core::MetalTexture& metal_texture, RefPtr<SkiaBackendContext> context)
{
IntSize const size { metal_texture.width(), metal_texture.height() };
auto image_info = SkImageInfo::Make(size.width(), size.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
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);
auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr);
return adopt_ref(*new PaintingSurface(make<Impl>(size, surface, nullptr, context)));
}
#endif
PaintingSurface::PaintingSurface(NonnullOwnPtr<Impl>&& impl)
: m_impl(move(impl))
{
}
PaintingSurface::~PaintingSurface() = default;
RefPtr<Bitmap> PaintingSurface::create_snapshot() const
{
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size()).value();
auto image_info = SkImageInfo::Make(bitmap->width(), bitmap->height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
SkPixmap const pixmap(image_info, bitmap->begin(), bitmap->pitch());
sk_surface().readPixels(pixmap, 0, 0);
return bitmap;
}
void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap)
{
auto color_type = to_skia_color_type(bitmap.format());
auto alpha_type = bitmap.alpha_type() == Gfx::AlphaType::Premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type);
SkPixmap const pixmap(image_info, bitmap.begin(), bitmap.pitch());
m_impl->surface->readPixels(pixmap, 0, 0);
}
IntSize PaintingSurface::size() const
{
return m_impl->size;
}
IntRect PaintingSurface::rect() const
{
return { {}, m_impl->size };
}
SkCanvas& PaintingSurface::canvas() const
{
return *m_impl->surface->getCanvas();
}
SkSurface& PaintingSurface::sk_surface() const
{
return *m_impl->surface;
}
void PaintingSurface::flush() const
{
if (auto context = m_impl->context) {
context->flush_and_submit(m_impl->surface.get());
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <LibGfx/Color.h>
#include <LibGfx/Size.h>
#include <LibGfx/SkiaBackendContext.h>
#ifdef AK_OS_MACOS
# include <LibCore/MetalContext.h>
#endif
class SkCanvas;
class SkSurface;
namespace Gfx {
class PaintingSurface : public RefCounted<PaintingSurface> {
public:
static NonnullRefPtr<PaintingSurface> create_with_size(RefPtr<SkiaBackendContext> context, Gfx::IntSize size, Gfx::BitmapFormat color_type, Gfx::AlphaType alpha_type);
static NonnullRefPtr<PaintingSurface> wrap_bitmap(Bitmap&);
#ifdef AK_OS_MACOS
static NonnullRefPtr<PaintingSurface> wrap_metal_surface(Core::MetalTexture&, RefPtr<SkiaBackendContext>);
#endif
RefPtr<Bitmap> create_snapshot() const;
void read_into_bitmap(Bitmap&);
IntSize size() const;
IntRect rect() const;
SkCanvas& canvas() const;
SkSurface& sk_surface() const;
void flush() const;
~PaintingSurface();
private:
struct Impl;
PaintingSurface(NonnullOwnPtr<Impl>&&);
NonnullOwnPtr<Impl> m_impl;
};
}

View file

@ -10,6 +10,7 @@
#include <AK/RefPtr.h>
#include <LibGfx/SkiaBackendContext.h>
#include <core/SkSurface.h>
#include <gpu/GrDirectContext.h>
#ifdef USE_VULKAN
@ -41,18 +42,13 @@ public:
~SkiaVulkanBackendContext() override { }
void flush_and_submit() override
void flush_and_submit(SkSurface* surface) override
{
m_context->flush();
GrFlushInfo const flush_info {};
m_context->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, flush_info);
m_context->submit(GrSyncCpu::kYes);
}
sk_sp<SkSurface> create_surface(int width, int height)
{
auto image_info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType);
return SkSurfaces::RenderTarget(m_context.get(), skgpu::Budgeted::kYes, image_info);
}
skgpu::VulkanExtensions const* extensions() const { return m_extensions.ptr(); }
GrDirectContext* sk_context() const override { return m_context.get(); }
@ -100,9 +96,10 @@ public:
~SkiaMetalBackendContext() override { }
void flush_and_submit() override
void flush_and_submit(SkSurface* surface) override
{
m_context->flush();
GrFlushInfo const flush_info {};
m_context->flush(surface, SkSurfaces::BackendSurfaceAccess::kPresent, flush_info);
m_context->submit(GrSyncCpu::kYes);
}

View file

@ -18,6 +18,7 @@
#endif
class GrDirectContext;
class SkSurface;
namespace Gfx {
@ -37,7 +38,7 @@ public:
SkiaBackendContext() {};
virtual ~SkiaBackendContext() {};
virtual void flush_and_submit() {};
virtual void flush_and_submit(SkSurface*) {};
virtual GrDirectContext* sk_context() const = 0;
};

View file

@ -1402,7 +1402,8 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
if (m_metal_context && m_skia_backend_context && is<Painting::IOSurfaceBackingStore>(target)) {
auto& iosurface_backing_store = static_cast<Painting::IOSurfaceBackingStore&>(target);
auto texture = m_metal_context->create_texture_from_iosurface(iosurface_backing_store.iosurface_handle());
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, *texture);
auto painting_surface = Gfx::PaintingSurface::wrap_metal_surface(*texture, m_skia_backend_context);
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, painting_surface);
player.execute(*display_list);
return;
}

View file

@ -30,92 +30,38 @@
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
#include <LibWeb/Painting/ShadowPainting.h>
#ifdef USE_VULKAN
# include <gpu/ganesh/vk/GrVkDirectContext.h>
# include <gpu/vk/VulkanBackendContext.h>
# include <gpu/vk/VulkanExtensions.h>
#endif
#ifdef AK_OS_MACOS
# include <gpu/GrBackendSurface.h>
# include <gpu/ganesh/mtl/GrMtlBackendContext.h>
# include <gpu/ganesh/mtl/GrMtlBackendSurface.h>
# include <gpu/ganesh/mtl/GrMtlDirectContext.h>
#endif
namespace Web::Painting {
class DisplayListPlayerSkia::SkiaSurface {
public:
SkCanvas& canvas() const { return *m_surface->getCanvas(); }
SkiaSurface(sk_sp<SkSurface> surface)
: m_surface(move(surface))
{
}
void read_into_bitmap(Gfx::Bitmap& bitmap)
{
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
SkPixmap pixmap(image_info, bitmap.begin(), bitmap.pitch());
m_surface->readPixels(pixmap, 0, 0);
}
sk_sp<SkSurface> make_surface(int width, int height)
{
return m_surface->makeSurface(width, height);
}
private:
sk_sp<SkSurface> m_surface;
};
#ifdef USE_VULKAN
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, Gfx::Bitmap& bitmap)
: m_context(context)
{
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
auto surface = SkSurfaces::RenderTarget(context.sk_context(), skgpu::Budgeted::kYes, image_info);
m_surface = make<SkiaSurface>(surface);
m_flush_context = [&bitmap, &surface = m_surface, &context] {
context.flush_and_submit();
m_surface = Gfx::PaintingSurface::create_with_size(m_context, bitmap.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
m_flush_context = [&bitmap, surface = m_surface] mutable {
surface->read_into_bitmap(bitmap);
};
}
#endif
#ifdef AK_OS_MACOS
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, Core::MetalTexture& metal_texture)
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, NonnullRefPtr<Gfx::PaintingSurface> surface)
: m_context(context)
, m_surface(move(surface))
{
auto image_info = SkImageInfo::Make(metal_texture.width(), metal_texture.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
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);
auto surface = SkSurfaces::WrapBackendRenderTarget(context.sk_context(), backend_render_target, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr);
if (!surface) {
dbgln("Failed to create Skia surface from Metal texture");
VERIFY_NOT_REACHED();
}
m_surface = make<SkiaSurface>(surface);
m_flush_context = [&context] mutable {
context.flush_and_submit();
};
}
#endif
DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::Bitmap& bitmap)
{
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch());
VERIFY(surface);
m_surface = make<SkiaSurface>(surface);
m_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
}
DisplayListPlayerSkia::~DisplayListPlayerSkia()
{
if (m_flush_context)
m_surface->flush();
if (m_flush_context) {
m_flush_context();
}
}
static SkPoint to_skia_point(auto const& point)
@ -357,9 +303,9 @@ static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode)
}
}
DisplayListPlayerSkia::SkiaSurface& DisplayListPlayerSkia::surface() const
Gfx::PaintingSurface& DisplayListPlayerSkia::surface() const
{
return static_cast<SkiaSurface&>(*m_surface);
return *m_surface;
}
void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
@ -1145,16 +1091,16 @@ void DisplayListPlayerSkia::add_mask(AddMask const& command)
if (rect.is_empty())
return;
auto mask_surface = m_surface->make_surface(rect.width(), rect.height());
auto mask_surface = Gfx::PaintingSurface::create_with_size(m_context, rect.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
auto previous_surface = move(m_surface);
m_surface = make<SkiaSurface>(mask_surface);
m_surface = mask_surface;
execute(*command.display_list);
m_surface = move(previous_surface);
SkMatrix mask_matrix;
mask_matrix.setTranslate(rect.x(), rect.y());
auto image = mask_surface->makeImageSnapshot();
auto image = mask_surface->sk_surface().makeImageSnapshot();
auto shader = image->makeShader(SkSamplingOptions(), mask_matrix);
surface().canvas().clipShader(shader);
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <LibGfx/SkiaBackendContext.h>
#include <LibWeb/Painting/DisplayListRecorder.h>
@ -23,7 +24,7 @@ public:
#endif
#ifdef AK_OS_MACOS
DisplayListPlayerSkia(Gfx::SkiaBackendContext&, Core::MetalTexture&);
DisplayListPlayerSkia(Gfx::SkiaBackendContext&, NonnullRefPtr<Gfx::PaintingSurface>);
#endif
virtual ~DisplayListPlayerSkia() override;
@ -67,10 +68,11 @@ private:
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
class SkiaSurface;
SkiaSurface& surface() const;
Gfx::PaintingSurface& surface() const;
RefPtr<Gfx::SkiaBackendContext> m_context {};
RefPtr<Gfx::PaintingSurface> m_surface {};
OwnPtr<SkiaSurface> m_surface;
Function<void()> m_flush_context;
};