From be9ee28afc55ea0554d4965205373f1c7599308b Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 15 Jun 2026 23:31:40 +0200 Subject: [PATCH] LibGfx: Stop caching Skia images during canvas playback The decoded-frame Skia image cache is useful for display-list rasterization because decoded image resources can be replayed over many frames. The cache lets that path reuse SkImage wrappers and GPU-backed copies instead of rebuilding them whenever the same resource is painted. For canvas, commands are consumed into one backing surface and decoded frames are already held by the command or paint style for the draw. A per-painter cache does not match that usage model, and can keep decoded frames and Skia images alive after the draw has consumed them. This removes the cache from PainterSkia and drops the now-unused pruning hook from CanvasCommandPlayer. With the cache gone, PainterSkia can hold its painting surface directly instead of allocating a private Impl. DisplayListPlayerSkia keeps owning the cache, so display-list rasterization keeps the SkImage reuse behavior. --- Libraries/LibGfx/CanvasCommandPlayer.cpp | 5 -- Libraries/LibGfx/CanvasCommandPlayer.h | 2 - Libraries/LibGfx/PainterSkia.cpp | 63 ++++++++----------- Libraries/LibGfx/PainterSkia.h | 7 +-- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 1 - 5 files changed, 27 insertions(+), 51 deletions(-) diff --git a/Libraries/LibGfx/CanvasCommandPlayer.cpp b/Libraries/LibGfx/CanvasCommandPlayer.cpp index bcf939a7ac..e75da2a8f7 100644 --- a/Libraries/LibGfx/CanvasCommandPlayer.cpp +++ b/Libraries/LibGfx/CanvasCommandPlayer.cpp @@ -37,11 +37,6 @@ void CanvasCommandPlayer::play(CanvasCommandList const& command_list) command.visit([&](auto const& command) { play_command(command); }); } -void CanvasCommandPlayer::prune_caches() -{ - m_painter->prune_caches(); -} - void CanvasCommandPlayer::play_command(CanvasCommands::ClearRect const& command) { m_painter->clear_rect(command.rect, command.color); diff --git a/Libraries/LibGfx/CanvasCommandPlayer.h b/Libraries/LibGfx/CanvasCommandPlayer.h index 4cb0917ec4..a5d5c2aa24 100644 --- a/Libraries/LibGfx/CanvasCommandPlayer.h +++ b/Libraries/LibGfx/CanvasCommandPlayer.h @@ -29,8 +29,6 @@ public: void play(CanvasCommandList const&); - void prune_caches(); - private: void play_command(CanvasCommands::ClearRect const&); void play_command(CanvasCommands::FillRect const&); diff --git a/Libraries/LibGfx/PainterSkia.cpp b/Libraries/LibGfx/PainterSkia.cpp index 9a50975d19..1eab4ac392 100644 --- a/Libraries/LibGfx/PainterSkia.cpp +++ b/Libraries/LibGfx/PainterSkia.cpp @@ -11,11 +11,9 @@ #define SK_SUPPORT_UNSPANNED_APIS #include -#include #include #include #include -#include #include #include #include @@ -30,18 +28,12 @@ namespace Gfx { -struct PainterSkia::Impl { - RefPtr painting_surface; - DecodedImageFrameSkiaImageCache image_cache; +static sk_sp sk_image_for_decoded_image_frame(DecodedImageFrame const& frame) +{ + return sk_image_from_bitmap(frame.bitmap(), frame.color_space()); +} - Impl(Gfx::PaintingSurface& surface) - : painting_surface(surface) - , image_cache(surface.skia_backend_context()) - { - } -}; - -static void apply_paint_style(SkPaint& paint, PaintStyle const& style, DecodedImageFrameSkiaImageCache& image_cache) +static void apply_paint_style(SkPaint& paint, PaintStyle const& style) { if (auto const& solid_color = as_if(style)) { paint.setColor(to_skia_color(solid_color->color())); @@ -107,7 +99,7 @@ static void apply_paint_style(SkPaint& paint, PaintStyle const& style, DecodedIm auto frame = canvas_pattern->image(); if (!frame.has_value()) return; - auto sk_image = image_cache.image_for_frame(*frame); + auto sk_image = sk_image_for_decoded_image_frame(*frame); if (!sk_image) return; @@ -141,11 +133,11 @@ static void apply_filter(SkPaint& paint, Gfx::Filter const& filter) paint.setImageFilter(to_skia_image_filter(filter)); } -static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional filter, DecodedImageFrameSkiaImageCache& image_cache) +static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional filter) { SkPaint paint; - apply_paint_style(paint, style, image_cache); + apply_paint_style(paint, style); if (filter.has_value()) apply_filter(paint, move(filter.value())); @@ -154,21 +146,16 @@ static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional painting_surface) - : m_impl(adopt_own(*new Impl { move(painting_surface) })) + : m_painting_surface(move(painting_surface)) { - m_initial_save_count = m_impl->painting_surface->canvas().save(); + m_initial_save_count = m_painting_surface->canvas().save(); } PainterSkia::~PainterSkia() = default; -void PainterSkia::prune_caches() -{ - impl().image_cache.prune(); -} - void PainterSkia::clear_rect(Gfx::FloatRect const& rect, Gfx::Color color) { - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.save(); canvas.clipRect(to_skia_rect(rect)); canvas.clear(to_skia_color(color)); @@ -179,7 +166,7 @@ void PainterSkia::fill_rect(Gfx::FloatRect const& rect, Color color) { SkPaint paint; paint.setColor(to_skia_color(color)); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawRect(to_skia_rect(rect), paint); } @@ -193,11 +180,11 @@ void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::DecodedImageF paint.setAlpha(static_cast(global_alpha * 255)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); - auto sk_image = impl().image_cache.image_for_frame(source); + auto sk_image = sk_image_for_decoded_image_frame(source); if (!sk_image) return; - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawImageRect( sk_image.get(), to_skia_rect(src_rect), @@ -214,7 +201,7 @@ void PainterSkia::set_transform(Gfx::AffineTransform const& transform) transform.b(), transform.d(), transform.f(), 0, 0, 1); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.setMatrix(matrix); } @@ -236,7 +223,7 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thi paint.setPathEffect(SkDashPathEffect::Make(dash_array.data(), dash_array.size(), dash_offset)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto sk_path = to_skia_path(path); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } @@ -247,7 +234,7 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& pain return; auto sk_path = to_skia_path(path); - auto paint = to_skia_paint(paint_style, filter, impl().image_cache); + auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); @@ -258,7 +245,7 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& pain paint.setStrokeMiter(miter_limit); paint.setPathEffect(SkDashPathEffect::Make(dash_array.data(), dash_array.size(), dash_offset)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } @@ -271,7 +258,7 @@ void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::Windin paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } @@ -279,24 +266,24 @@ void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_ { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); - auto paint = to_skia_paint(paint_style, filter, impl().image_cache); + auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } void PainterSkia::save() { - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.save(); } void PainterSkia::restore() { - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.restore(); } @@ -304,13 +291,13 @@ void PainterSkia::clip(Gfx::Path const& path, Gfx::WindingRule winding_rule) { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.clipPath(sk_path, SkClipOp::kIntersect, true); } void PainterSkia::reset() { - auto& canvas = impl().painting_surface->canvas(); + auto& canvas = m_painting_surface->canvas(); canvas.restoreToCount(m_initial_save_count); } diff --git a/Libraries/LibGfx/PainterSkia.h b/Libraries/LibGfx/PainterSkia.h index 9bfd370cea..cbd6b1689f 100644 --- a/Libraries/LibGfx/PainterSkia.h +++ b/Libraries/LibGfx/PainterSkia.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include #include @@ -33,12 +33,9 @@ public: void restore(); void clip(Gfx::Path const&, Gfx::WindingRule); void reset(); - void prune_caches(); private: - struct Impl; - Impl& impl() { return *m_impl; } - NonnullOwnPtr m_impl; + NonnullRefPtr m_painting_surface; u32 m_initial_save_count { 0 }; }; diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 8ff5114fdf..85862c1573 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -278,7 +278,6 @@ void CanvasRenderingContext2D::present() if (!m_player) return; flush_recorded_commands(); - m_player->prune_caches(); } void CanvasRenderingContext2D::allocate_painting_surface_if_needed()