mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
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:
parent
5c1f6bf8ee
commit
25f264b7e7
Notes:
github-actions[bot]
2024-11-07 12:49:32 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/25f264b7e7a Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1532 Reviewed-by: https://github.com/ADKaster
8 changed files with 227 additions and 85 deletions
|
@ -53,6 +53,7 @@ set(SOURCES
|
|||
ImageFormats/AVIFLoader.cpp
|
||||
ImmutableBitmap.cpp
|
||||
MedianCut.cpp
|
||||
PaintingSurface.cpp
|
||||
Palette.cpp
|
||||
Path.cpp
|
||||
PathSkia.cpp
|
||||
|
|
139
Userland/Libraries/LibGfx/PaintingSurface.cpp
Normal file
139
Userland/Libraries/LibGfx/PaintingSurface.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
55
Userland/Libraries/LibGfx/PaintingSurface.h
Normal file
55
Userland/Libraries/LibGfx/PaintingSurface.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue