diff --git a/Libraries/LibGfx/PaintingSurface.cpp b/Libraries/LibGfx/PaintingSurface.cpp index f061299be94d5..35e131d590eda 100644 --- a/Libraries/LibGfx/PaintingSurface.cpp +++ b/Libraries/LibGfx/PaintingSurface.cpp @@ -24,7 +24,6 @@ struct PaintingSurface::Impl { IntSize size; sk_sp surface; RefPtr bitmap; - RefPtr context; }; NonnullRefPtr PaintingSurface::create_with_size(RefPtr context, IntSize size, BitmapFormat color_type, AlphaType alpha_type) @@ -37,12 +36,12 @@ NonnullRefPtr PaintingSurface::create_with_size(RefPtrbegin(), bitmap->pitch()); VERIFY(surface); - return adopt_ref(*new PaintingSurface(make(size, surface, bitmap, context))); + return adopt_ref(*new PaintingSurface(make(size, surface, bitmap))); } auto surface = SkSurfaces::RenderTarget(context->sk_context(), skgpu::Budgeted::kNo, image_info); VERIFY(surface); - return adopt_ref(*new PaintingSurface(make(size, surface, nullptr, context))); + return adopt_ref(*new PaintingSurface(make(size, surface, nullptr))); } NonnullRefPtr PaintingSurface::wrap_bitmap(Bitmap& bitmap) @@ -52,7 +51,7 @@ NonnullRefPtr PaintingSurface::wrap_bitmap(Bitmap& bitmap) auto size = bitmap.size(); auto image_info = SkImageInfo::Make(bitmap.width(), bitmap.height(), color_type, alpha_type, SkColorSpace::MakeSRGB()); auto surface = SkSurfaces::WrapPixels(image_info, bitmap.begin(), bitmap.pitch()); - return adopt_ref(*new PaintingSurface(make(size, surface, bitmap, nullptr))); + return adopt_ref(*new PaintingSurface(make(size, surface, bitmap))); } #ifdef AK_OS_MACOS @@ -76,7 +75,7 @@ NonnullRefPtr PaintingSurface::wrap_iosurface(Core::IOSurfaceHa VERIFY_NOT_REACHED(); } auto surface = SkSurfaces::WrapBackendRenderTarget(context->sk_context(), backend_render_target, sk_origin, kBGRA_8888_SkColorType, nullptr, nullptr); - return adopt_ref(*new PaintingSurface(make(size, surface, nullptr, context))); + return adopt_ref(*new PaintingSurface(make(size, surface, nullptr))); } #endif @@ -138,9 +137,8 @@ sk_sp PaintingSurface::sk_image_snapshot() const void PaintingSurface::flush() const { - if (auto context = m_impl->context) { - context->flush_and_submit(m_impl->surface.get()); - } + if (on_flush) + on_flush(); } } diff --git a/Libraries/LibGfx/PaintingSurface.h b/Libraries/LibGfx/PaintingSurface.h index 19778a3dbdcc5..a42590cd7b3c6 100644 --- a/Libraries/LibGfx/PaintingSurface.h +++ b/Libraries/LibGfx/PaintingSurface.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -29,6 +30,8 @@ class PaintingSurface : public RefCounted { BottomLeft, }; + Function on_flush; + static NonnullRefPtr create_with_size(RefPtr context, IntSize size, BitmapFormat color_type, AlphaType alpha_type); static NonnullRefPtr wrap_bitmap(Bitmap&); diff --git a/Libraries/LibGfx/SkiaBackendContext.cpp b/Libraries/LibGfx/SkiaBackendContext.cpp index 163c9d42e0a76..40528797744b7 100644 --- a/Libraries/LibGfx/SkiaBackendContext.cpp +++ b/Libraries/LibGfx/SkiaBackendContext.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 455a9177e1770..80ad756612f0a 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -53,8 +53,12 @@ TraversableNavigable::TraversableNavigable(GC::Ref page) , m_session_history_traversal_queue(vm().heap().allocate()) { auto display_list_player_type = page->client().display_list_player_type(); - if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable) + if (display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable) { m_skia_backend_context = get_skia_backend_context(); + m_skia_player = make(m_skia_backend_context); + } else { + m_skia_player = make(); + } } TraversableNavigable::~TraversableNavigable() = default; @@ -1384,6 +1388,44 @@ GC::Ptr TraversableNavigable::currently_focused_area() return candidate; } +NonnullRefPtr TraversableNavigable::painting_surface_for_backing_store(Painting::BackingStore& backing_store) +{ + // Invalidate the surface cache if any provided backing store has a different size. This is a simple way to handle + // resizes of the navigable and prevents the cache from growing unbounded. + if (m_last_paint_size != backing_store.size()) { + m_backing_store_to_surface.clear(); + m_last_paint_size = backing_store.size(); + } + + // auto cached_surface = m_backing_store_to_surface.find(&backing_store); + // if (cached_surface != m_backing_store_to_surface.end()) + // return cached_surface->value; + + RefPtr new_surface; + if (page().client().display_list_player_type() == DisplayListPlayerType::SkiaGPUIfAvailable) { +#ifdef USE_VULKAN + // Vulkan: Try to create an accelerated surface. + if (m_skia_backend_context) { + new_surface = Gfx::PaintingSurface::create_with_size(m_skia_backend_context, backing_store.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied); + new_surface->on_flush = [&bitmap = backing_store.bitmap(), new_surface] { new_surface->read_into_bitmap(bitmap); }; + } +#elif AK_OS_MACOS + // macOS: Wrap an IOSurface if available. + if (m_skia_backend_context && is(target)) { + auto& iosurface_backing_store = static_cast(target); + new_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context); + } +#endif + } + + // CPU and fallback: wrap the backing store bitmap directly. + if (!new_surface) + new_surface = Gfx::PaintingSurface::wrap_bitmap(backing_store.bitmap()); + + m_backing_store_to_surface.set(&backing_store, *new_surface); + return *new_surface; +} + void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::BackingStore& target, PaintOptions paint_options) { auto document = active_document(); @@ -1391,9 +1433,8 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting:: return; for (auto& navigable : all_navigables()) { - if (auto active_document = navigable->active_document(); active_document && active_document->paintable()) { + if (auto active_document = navigable->active_document(); active_document && active_document->paintable()) active_document->paintable()->refresh_scroll_state(); - } } DOM::Document::PaintConfig paint_config; @@ -1402,43 +1443,12 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting:: paint_config.has_focus = paint_options.has_focus; paint_config.canvas_fill_rect = Gfx::IntRect { {}, content_rect.size() }; auto display_list = document->record_display_list(paint_config); - if (!display_list) { + if (!display_list) return; - } - - switch (page().client().display_list_player_type()) { - case DisplayListPlayerType::SkiaGPUIfAvailable: { -#ifdef USE_VULKAN - if (m_skia_backend_context) { - Painting::DisplayListPlayerSkia player(*m_skia_backend_context, target.bitmap()); - player.execute(*display_list); - return; - } -#endif - -#ifdef AK_OS_MACOS - if (m_skia_backend_context && is(target)) { - auto& iosurface_backing_store = static_cast(target); - auto painting_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context); - Painting::DisplayListPlayerSkia player(*m_skia_backend_context, painting_surface); - player.execute(*display_list); - return; - } -#endif - // Fallback to CPU backend if GPU is not available - Painting::DisplayListPlayerSkia player(target.bitmap()); - player.execute(*display_list); - break; - } - case DisplayListPlayerType::SkiaCPU: { - Painting::DisplayListPlayerSkia player(target.bitmap()); - player.execute(*display_list); - break; - } - default: - VERIFY_NOT_REACHED(); - } + auto painting_surface = painting_surface_for_backing_store(target); + m_skia_player->set_surface(painting_surface); + m_skia_player->execute(*display_list); } } diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.h b/Libraries/LibWeb/HTML/TraversableNavigable.h index 7f30026abe5ac..6b46aef29ff49 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.h +++ b/Libraries/LibWeb/HTML/TraversableNavigable.h @@ -14,15 +14,16 @@ #include #include #include - -#ifdef USE_VULKAN -# include -#endif +#include #ifdef AK_OS_MACOS # include #endif +#ifdef USE_VULKAN +# include +#endif + namespace Web::HTML { // https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable @@ -131,6 +132,8 @@ class TraversableNavigable final : public Navigable { [[nodiscard]] bool can_go_forward() const; + NonnullRefPtr painting_surface_for_backing_store(Painting::BackingStore&); + // https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step int m_current_session_history_step { 0 }; @@ -154,6 +157,9 @@ class TraversableNavigable final : public Navigable { String m_window_handle; RefPtr m_skia_backend_context; + OwnPtr m_skia_player; + HashMap> m_backing_store_to_surface; + Gfx::IntSize m_last_paint_size; }; struct BrowsingContextAndDocument { diff --git a/Libraries/LibWeb/Painting/DisplayList.cpp b/Libraries/LibWeb/Painting/DisplayList.cpp index ec7ad75e93b69..8029b08e38aff 100644 --- a/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Libraries/LibWeb/Painting/DisplayList.cpp @@ -41,6 +41,8 @@ void DisplayListPlayer::execute(DisplayList& display_list) auto const& scroll_state = display_list.scroll_state(); auto device_pixels_per_css_pixel = display_list.device_pixels_per_css_pixel(); + VERIFY(m_surface); + size_t next_command_index = 0; while (next_command_index < commands.size()) { auto scroll_frame_id = commands[next_command_index].scroll_frame_id; @@ -128,6 +130,8 @@ void DisplayListPlayer::execute(DisplayList& display_list) else VERIFY_NOT_REACHED(); // clang-format on } + + flush(); } } diff --git a/Libraries/LibWeb/Painting/DisplayList.h b/Libraries/LibWeb/Painting/DisplayList.h index a26ed493e013d..188d18805b71c 100644 --- a/Libraries/LibWeb/Painting/DisplayList.h +++ b/Libraries/LibWeb/Painting/DisplayList.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -25,9 +26,14 @@ class DisplayListPlayer { public: virtual ~DisplayListPlayer() = default; - void execute(DisplayList& display_list); + void execute(DisplayList&); + void set_surface(NonnullRefPtr surface) { m_surface = surface; } + +protected: + Gfx::PaintingSurface& surface() const { return *m_surface; } private: + virtual void flush() = 0; virtual void draw_glyph_run(DrawGlyphRun const&) = 0; virtual void fill_rect(FillRect const&) = 0; virtual void draw_painting_surface(DrawPaintingSurface const&) = 0; @@ -65,6 +71,8 @@ class DisplayListPlayer { virtual void apply_transform(ApplyTransform const&) = 0; virtual void apply_mask_bitmap(ApplyMaskBitmap const&) = 0; virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0; + + RefPtr m_surface; }; class DisplayList : public RefCounted { diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index e7bf24221220b..654b07b6047d5 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -32,36 +33,13 @@ namespace Web::Painting { -#ifdef USE_VULKAN -DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::SkiaBackendContext& context, Gfx::Bitmap& bitmap) +DisplayListPlayerSkia::DisplayListPlayerSkia(RefPtr context) : m_context(context) -{ - 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, NonnullRefPtr surface) - : m_context(context) - , m_surface(move(surface)) { } -#endif -DisplayListPlayerSkia::DisplayListPlayerSkia(Gfx::Bitmap& bitmap) +DisplayListPlayerSkia::DisplayListPlayerSkia() { - m_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap); -} - -DisplayListPlayerSkia::~DisplayListPlayerSkia() -{ - m_surface->flush(); - if (m_flush_context) { - m_flush_context(); - } } static SkRRect to_skia_rrect(auto const& rect, CornerRadii const& corner_radii) @@ -91,9 +69,11 @@ static SkMatrix to_skia_matrix(Gfx::AffineTransform const& affine_transform) return matrix; } -Gfx::PaintingSurface& DisplayListPlayerSkia::surface() const +void DisplayListPlayerSkia::flush() { - return *m_surface; + if (m_context) + m_context->flush_and_submit(&surface().sk_surface()); + surface().flush(); } void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command) @@ -853,10 +833,10 @@ void DisplayListPlayerSkia::add_mask(AddMask const& command) 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 = mask_surface; + NonnullRefPtr old_surface = surface(); + set_surface(mask_surface); execute(*command.display_list); - m_surface = move(previous_surface); + set_surface(old_surface); SkMatrix mask_matrix; mask_matrix.setTranslate(rect.x(), rect.y()); diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index d70400bc73aa0..2e567647c8185 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -14,21 +14,13 @@ class GrDirectContext; namespace Web::Painting { -class DisplayListPlayerSkia : public DisplayListPlayer { +class DisplayListPlayerSkia final : public DisplayListPlayer { public: - DisplayListPlayerSkia(Gfx::Bitmap&); - -#ifdef USE_VULKAN - DisplayListPlayerSkia(Gfx::SkiaBackendContext&, Gfx::Bitmap&); -#endif - -#ifdef AK_OS_MACOS - DisplayListPlayerSkia(Gfx::SkiaBackendContext&, NonnullRefPtr); -#endif - - virtual ~DisplayListPlayerSkia() override; + DisplayListPlayerSkia(RefPtr); + DisplayListPlayerSkia(); private: + void flush() override; void draw_glyph_run(DrawGlyphRun const&) override; void fill_rect(FillRect const&) override; void draw_painting_surface(DrawPaintingSurface const&) override; @@ -68,12 +60,7 @@ class DisplayListPlayerSkia : public DisplayListPlayer { bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override; - Gfx::PaintingSurface& surface() const; - - RefPtr m_context {}; - RefPtr m_surface {}; - - Function m_flush_context; + RefPtr m_context; }; } diff --git a/Libraries/LibWeb/Painting/SVGMaskable.cpp b/Libraries/LibWeb/Painting/SVGMaskable.cpp index 68d13b57bafc3..e841d31c938a1 100644 --- a/Libraries/LibWeb/Painting/SVGMaskable.cpp +++ b/Libraries/LibWeb/Painting/SVGMaskable.cpp @@ -90,7 +90,9 @@ RefPtr SVGMaskable::calculate_mask_of_svg(PaintContext& co paint_context.set_svg_transform(graphics_element.get_transform()); paint_context.set_draw_svg_geometry_for_clip_path(is(paintable)); StackingContext::paint_svg(paint_context, paintable, PaintPhase::Foreground); - DisplayListPlayerSkia display_list_player { *mask_bitmap }; + auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(*mask_bitmap); + DisplayListPlayerSkia display_list_player; + display_list_player.set_surface(painting_surface); display_list_player.execute(display_list); return mask_bitmap; }; diff --git a/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp b/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp index 163930d087619..db2191875fc37 100644 --- a/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp +++ b/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp @@ -103,7 +103,9 @@ RefPtr SVGDecodedImageData::render(Gfx::IntSize size) const switch (painting_command_executor_type) { case DisplayListPlayerType::SkiaGPUIfAvailable: case DisplayListPlayerType::SkiaCPU: { - Painting::DisplayListPlayerSkia display_list_player { *bitmap }; + auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(*bitmap); + Painting::DisplayListPlayerSkia display_list_player; + display_list_player.set_surface(painting_surface); display_list_player.execute(*display_list); break; }