From 4b93e276985d87520b9ff4efa883fa57325233b5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 9 Nov 2024 06:47:16 +0100 Subject: [PATCH] LibGfx+LibWeb: Create PaintingSurface snapshot without GPU->RAM readback ...by constructing ImmutableBitmap directly from SkImage. This is a huge optimization for the case when content of canvas is painted onto another canvas, as it allows pixels to remain in GPU memory throughout the process. Fixes performance regression on https://playbiolab.com/ introduced by switching to GPU-backend for canvas. --- Userland/Libraries/LibGfx/ImmutableBitmap.cpp | 22 +++++++++++++------ Userland/Libraries/LibGfx/ImmutableBitmap.h | 1 + Userland/Libraries/LibGfx/PaintingSurface.cpp | 15 +++++-------- Userland/Libraries/LibGfx/PaintingSurface.h | 4 +++- .../Libraries/LibWeb/HTML/CanvasPattern.cpp | 2 +- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 4 ++-- .../LibWeb/HTML/HTMLCanvasElement.cpp | 2 +- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Userland/Libraries/LibGfx/ImmutableBitmap.cpp b/Userland/Libraries/LibGfx/ImmutableBitmap.cpp index 79a52b15210..67dc2bf5077 100644 --- a/Userland/Libraries/LibGfx/ImmutableBitmap.cpp +++ b/Userland/Libraries/LibGfx/ImmutableBitmap.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -14,7 +15,7 @@ namespace Gfx { struct ImmutableBitmapImpl { sk_sp sk_image; SkBitmap sk_bitmap; - RefPtr gfx_bitmap; + Variant, NonnullRefPtr, Empty> source; }; int ImmutableBitmap::width() const @@ -49,15 +50,14 @@ SkImage const* ImmutableBitmap::sk_image() const RefPtr ImmutableBitmap::bitmap() const { - return m_impl->gfx_bitmap; + // FIXME: Implement for PaintingSurface + return m_impl->source.get>(); } Color ImmutableBitmap::get_pixel(int x, int y) const { - if (m_impl->gfx_bitmap) { - return m_impl->gfx_bitmap->get_pixel(x, y); - } - VERIFY_NOT_REACHED(); + // FIXME: Implement for PaintingSurface + return m_impl->source.get>()->get_pixel(x, y); } static SkColorType to_skia_color_type(Gfx::BitmapFormat format) @@ -96,7 +96,15 @@ NonnullRefPtr ImmutableBitmap::create(NonnullRefPtr bit impl.sk_bitmap.installPixels(info, const_cast(static_cast(bitmap->scanline(0))), bitmap->pitch()); impl.sk_bitmap.setImmutable(); impl.sk_image = impl.sk_bitmap.asImage(); - impl.gfx_bitmap = bitmap; + impl.source = bitmap; + return adopt_ref(*new ImmutableBitmap(make(impl))); +} + +NonnullRefPtr ImmutableBitmap::create_snapshot_from_painting_surface(NonnullRefPtr painting_surface) +{ + ImmutableBitmapImpl impl; + impl.sk_image = painting_surface->sk_image_snapshot>(); + impl.source = painting_surface; return adopt_ref(*new ImmutableBitmap(make(impl))); } diff --git a/Userland/Libraries/LibGfx/ImmutableBitmap.h b/Userland/Libraries/LibGfx/ImmutableBitmap.h index e6b491c1e3d..e5eca00f760 100644 --- a/Userland/Libraries/LibGfx/ImmutableBitmap.h +++ b/Userland/Libraries/LibGfx/ImmutableBitmap.h @@ -22,6 +22,7 @@ struct ImmutableBitmapImpl; class ImmutableBitmap final : public RefCounted { public: static NonnullRefPtr create(NonnullRefPtr bitmap); + static NonnullRefPtr create_snapshot_from_painting_surface(NonnullRefPtr); ~ImmutableBitmap(); diff --git a/Userland/Libraries/LibGfx/PaintingSurface.cpp b/Userland/Libraries/LibGfx/PaintingSurface.cpp index 39b1a8db8c3..a94b14ab621 100644 --- a/Userland/Libraries/LibGfx/PaintingSurface.cpp +++ b/Userland/Libraries/LibGfx/PaintingSurface.cpp @@ -92,15 +92,6 @@ PaintingSurface::PaintingSurface(NonnullOwnPtr&& impl) PaintingSurface::~PaintingSurface() = default; -NonnullRefPtr 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 ImmutableBitmap::create(bitmap); -} - void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap) { auto color_type = to_skia_color_type(bitmap.format()); @@ -130,6 +121,12 @@ SkSurface& PaintingSurface::sk_surface() const return *m_impl->surface; } +template<> +sk_sp PaintingSurface::sk_image_snapshot() const +{ + return m_impl->surface->makeImageSnapshot(); +} + void PaintingSurface::flush() const { if (auto context = m_impl->context) { diff --git a/Userland/Libraries/LibGfx/PaintingSurface.h b/Userland/Libraries/LibGfx/PaintingSurface.h index 0b89c50dab8..eed6782a92c 100644 --- a/Userland/Libraries/LibGfx/PaintingSurface.h +++ b/Userland/Libraries/LibGfx/PaintingSurface.h @@ -31,7 +31,6 @@ public: static NonnullRefPtr wrap_metal_surface(Gfx::MetalTexture&, RefPtr); #endif - NonnullRefPtr create_snapshot() const; void read_into_bitmap(Bitmap&); IntSize size() const; @@ -40,6 +39,9 @@ public: SkCanvas& canvas() const; SkSurface& sk_surface() const; + template + T sk_image_snapshot() const; + void flush() const; ~PaintingSurface(); diff --git a/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp b/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp index 384f4c3787c..54fe700086d 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasPattern.cpp @@ -131,7 +131,7 @@ WebIDL::ExceptionOr> CanvasPattern::create(JS::Realm& r auto bitmap = image.visit( [](JS::Handle const& source) -> RefPtr { return source->immutable_bitmap(); }, [](JS::Handle const& source) -> RefPtr { return source->current_image_bitmap(); }, - [](JS::Handle const& source) -> RefPtr { return source->surface()->create_snapshot(); }, + [](JS::Handle const& source) -> RefPtr { return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*source->surface()); }, [](JS::Handle const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }, [](JS::Handle const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }); diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 811b9a4ccdb..351490247d3 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -134,7 +134,7 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm auto surface = source->surface(); if (!surface) return {}; - return source->surface()->create_snapshot(); + return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*surface); }, [](JS::Handle const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }, [](JS::Handle const& source) -> RefPtr { @@ -401,7 +401,7 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_da // NOTE: We don't attempt to create the underlying bitmap here; if it doesn't exist, it's like copying only transparent black pixels (which is a no-op). if (!canvas_element().surface()) return image_data; - auto const snapshot = canvas_element().surface()->create_snapshot(); + auto const snapshot = Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*canvas_element().surface()); // 5. Let the source rectangle be the rectangle whose corners are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh). auto source_rect = Gfx::Rect { x, y, abs_width, abs_height }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index e11f4bec403..9f9ca5d2ede 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -268,7 +268,7 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional quality) return "data:,"_string; // 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given. - auto snapshot = m_surface->create_snapshot(); + auto snapshot = Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*m_surface); auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, m_surface->size())); m_surface->read_into_bitmap(*bitmap); auto file = serialize_bitmap(bitmap, type, move(quality));