diff --git a/Libraries/LibGfx/PaintingSurface.cpp b/Libraries/LibGfx/PaintingSurface.cpp index 2c54030ffa..95d308602b 100644 --- a/Libraries/LibGfx/PaintingSurface.cpp +++ b/Libraries/LibGfx/PaintingSurface.cpp @@ -153,6 +153,13 @@ NonnullRefPtr PaintingSurface::snapshot_bitmap() const return bitmap; } +SharedImage PaintingSurface::snapshot_into_shared_image() const +{ + auto shared_image_buffer = SharedImageBuffer::create(size()); + read_into_bitmap(*shared_image_buffer.bitmap()); + return shared_image_buffer.export_shared_image(); +} + void PaintingSurface::read_into_bitmap(Bitmap& bitmap) const { auto color_type = to_skia_color_type(bitmap.format()); diff --git a/Libraries/LibGfx/PaintingSurface.h b/Libraries/LibGfx/PaintingSurface.h index 9a25670af7..3e55a5490b 100644 --- a/Libraries/LibGfx/PaintingSurface.h +++ b/Libraries/LibGfx/PaintingSurface.h @@ -31,6 +31,8 @@ class SkSurface; namespace Gfx { +class SharedImage; + class PaintingSurface : public AtomicRefCounted { public: enum class Origin { @@ -52,6 +54,7 @@ public: #endif NonnullRefPtr snapshot_bitmap() const; + SharedImage snapshot_into_shared_image() const; void read_into_bitmap(Bitmap&) const; void write_from_bitmap(Bitmap const&); diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 52db5ec07b..a10fc35ec0 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -861,7 +861,6 @@ set(SOURCES Painting/DisplayListRecorder.cpp Painting/DisplayListRecordingContext.cpp Painting/DisplayListResourceStorage.cpp - Painting/ExternalContentSource.cpp Painting/FieldSetPaintable.cpp Painting/GradientPainting.cpp Painting/ImagePaintable.cpp diff --git a/Libraries/LibWeb/Compositor/CompositorThread.cpp b/Libraries/LibWeb/Compositor/CompositorThread.cpp index 228afdfbcb..c971417f43 100644 --- a/Libraries/LibWeb/Compositor/CompositorThread.cpp +++ b/Libraries/LibWeb/Compositor/CompositorThread.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include #include @@ -67,6 +65,15 @@ struct UpdateScrollStateCommand { Painting::ScrollStateSnapshot scroll_state_snapshot; }; +struct UpdateCompositorSurfaceCommand { + Painting::CompositorSurfaceId surface_id; + Gfx::SharedImage shared_image; +}; + +struct ClearCompositorSurfaceCommand { + Painting::CompositorSurfaceId surface_id; +}; + struct ViewportSizeUpdatedCommand { Gfx::IntSize viewport_size; bool is_top_level_traversable { false }; @@ -79,7 +86,8 @@ struct ScreenshotCommand { }; using CompositorCommand = Variant; + UpdateScrollStateCommand, UpdateCompositorSurfaceCommand, ClearCompositorSurfaceCommand, ViewportSizeUpdatedCommand, + ScreenshotCommand>; static SkRect to_skia_rect(Gfx::IntRect const& rect) { @@ -714,6 +722,12 @@ public: } } }, + [this](UpdateCompositorSurfaceCommand& cmd) { + m_display_list_resource_storage.update_compositor_surface(cmd.surface_id, move(cmd.shared_image)); + }, + [this](ClearCompositorSurfaceCommand& cmd) { + m_display_list_resource_storage.clear_compositor_surface(cmd.surface_id); + }, [this](ViewportSizeUpdatedCommand& cmd) { auto allocation = m_backing_store_manager.resize_backing_stores_if_needed( cmd.viewport_size, cmd.is_top_level_traversable, cmd.window_resize_in_progress); @@ -1007,7 +1021,7 @@ private: if (m_cached_display_list && m_backing_store_manager.is_valid()) { auto should_clear_back_store = presentation_mode.visit( [](CompositorThread::PresentToUI) { return false; }, - [](CompositorThread::PublishToExternalContent const&) { return true; }); + [](CompositorThread::PublishToCompositorSurface const&) { return true; }); auto& back_store = m_backing_store_manager.back_store(); if (should_clear_back_store) { // Embedded navigables leave their PaintConfig canvas unfilled, so double-buffered back stores must be @@ -1036,15 +1050,16 @@ private: m_is_rasterizing = false; } }, - [this](CompositorThread::PublishToExternalContent const& mode) { + [this](CompositorThread::PublishToCompositorSurface const& mode) { { Sync::MutexLocker const locker { m_mutex }; m_is_rasterizing = false; } if (m_has_async_scrolling_state) - dbgln_if(COMPOSITOR_DEBUG, "[Compositor] Publishing present to external content source"); - auto snapshot = Gfx::DecodedImageFrame { *m_backing_store_manager.front_store().snapshot_bitmap() }; - mode.source->update(move(snapshot)); + dbgln_if(COMPOSITOR_DEBUG, "[Compositor] Publishing present to compositor surface"); + auto& front_store = m_backing_store_manager.front_store(); + VERIFY(CompositorThread::update_compositor_surface_for_context( + mode.target_context_id, mode.surface_id, front_store.snapshot_into_shared_image())); }); } else { { @@ -1186,6 +1201,12 @@ static HashMap>& page_composito return *compositors; } +static HashMap>& context_compositors() +{ + static NeverDestroyed>> compositors; + return *compositors; +} + static FramePresentationState& frame_presentation_state() { static NeverDestroyed state; @@ -1199,6 +1220,12 @@ CompositorThread::CompositorThread(u64 page_id, PagePresentationRegistration pag enqueue_viewport_size_updated(m_last_viewport_size, m_last_viewport_size_is_top_level_traversable, WindowResizingInProgress::No); }); + { + Sync::MutexLocker const locker { compositor_presentation_state_mutex() }; + VERIFY(!context_compositors().contains(m_context_id)); + context_compositors().set(m_context_id, m_thread_data); + } + if (page_presentation_registration == PagePresentationRegistration::Yes) register_page_compositor(page_id, m_thread_data); } @@ -1210,6 +1237,11 @@ CompositorThread::~CompositorThread() m_backing_store_shrink_timer.clear(); unregister_page_compositor(m_thread_data->page_id(), *m_thread_data); + { + Sync::MutexLocker const locker { compositor_presentation_state_mutex() }; + VERIFY(context_compositors().remove(m_context_id)); + } + m_thread_data->exit(); } @@ -1240,6 +1272,23 @@ void CompositorThread::unregister_page_compositor(u64 page_id, ThreadData& threa dbgln_if(COMPOSITOR_DEBUG, "[Compositor] Unregistered page {} from compositor presentation", page_id); } +bool CompositorThread::update_compositor_surface_for_context(CompositorContextId context_id, Painting::CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image) +{ + RefPtr thread_data; + { + Sync::MutexLocker const locker { compositor_presentation_state_mutex() }; + auto compositor = context_compositors().find(context_id); + if (compositor == context_compositors().end()) { + dbgln_if(COMPOSITOR_DEBUG, "[Compositor] Dropping compositor surface {} update for unknown context {}", + surface_id, context_id); + return false; + } + thread_data = compositor->value; + } + thread_data->enqueue_command(UpdateCompositorSurfaceCommand { surface_id, move(shared_image) }); + return true; +} + void CompositorThread::set_frame_presentation_callbacks(NonnullRefPtr event_loop, BackingStorePresentationCallback backing_store_callback, FramePresentationCallback frame_callback) { Sync::MutexLocker const locker { compositor_presentation_state_mutex() }; @@ -1443,6 +1492,16 @@ void CompositorThread::update_display_list( m_thread_data->enqueue_command(UpdateDisplayListCommand { move(display_list), move(resource_transaction), move(scroll_state_snapshot) }); } +void CompositorThread::update_compositor_surface(Painting::CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image) +{ + m_thread_data->enqueue_command(UpdateCompositorSurfaceCommand { surface_id, move(shared_image) }); +} + +void CompositorThread::clear_compositor_surface(Painting::CompositorSurfaceId surface_id) +{ + m_thread_data->enqueue_command(ClearCompositorSurfaceCommand { surface_id }); +} + void CompositorThread::invalidate_wheel_event_listener_state(u64 generation) { m_thread_data->invalidate_wheel_event_listener_state(generation); diff --git a/Libraries/LibWeb/Compositor/CompositorThread.h b/Libraries/LibWeb/Compositor/CompositorThread.h index ce6eea1fd0..82da4b04e5 100644 --- a/Libraries/LibWeb/Compositor/CompositorThread.h +++ b/Libraries/LibWeb/Compositor/CompositorThread.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -61,10 +62,11 @@ public: }; struct PresentToUI { }; - struct PublishToExternalContent { - NonnullRefPtr source; + struct PublishToCompositorSurface { + CompositorContextId target_context_id; + Painting::CompositorSurfaceId surface_id; }; - using PresentationMode = Variant; + using PresentationMode = Variant; CompositorThread(u64 page_id, PagePresentationRegistration); ~CompositorThread(); @@ -75,11 +77,14 @@ public: static bool async_scroll_by(u64 page_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels); static bool handle_mouse_event(u64 page_id, MouseEvent const&); + CompositorContextId context_id() const { return m_context_id; } void start(DisplayListPlayerType); void stop_presenting_to_client(); void set_presentation_mode(PresentationMode); void update_display_list(NonnullRefPtr, Painting::DisplayListResourceTransaction&&, Painting::ScrollStateSnapshot&&); + void update_compositor_surface(Painting::CompositorSurfaceId, Gfx::SharedImage&&); + void clear_compositor_surface(Painting::CompositorSurfaceId); void update_scroll_state(Painting::ScrollStateSnapshot&&); void invalidate_wheel_event_listener_state(u64 generation); AsyncScrollEnqueueResult async_scroll_by(UniqueNodeID expected_document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels, @@ -95,6 +100,7 @@ public: private: void enqueue_viewport_size_updated(Gfx::IntSize, bool is_top_level_traversable, WindowResizingInProgress); + CompositorContextId m_context_id { allocate_compositor_context_id() }; NonnullRefPtr m_thread_data; RefPtr m_thread; RefPtr m_backing_store_shrink_timer; @@ -103,6 +109,7 @@ private: static void register_page_compositor(u64 page_id, NonnullRefPtr); static void unregister_page_compositor(u64 page_id, ThreadData&); + static bool update_compositor_surface_for_context(CompositorContextId, Painting::CompositorSurfaceId, Gfx::SharedImage&&); static bool present_backing_stores_to_client(u64 page_id, i32 front_bitmap_id, Gfx::SharedImage&&, i32 back_bitmap_id, Gfx::SharedImage&&); static bool present_frame_to_client(u64 page_id, Gfx::IntRect const&, i32 bitmap_id); }; diff --git a/Libraries/LibWeb/Compositor/Types.h b/Libraries/LibWeb/Compositor/Types.h new file mode 100644 index 0000000000..c4f0915a73 --- /dev/null +++ b/Libraries/LibWeb/Compositor/Types.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026, Ladybird contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Compositor { + +AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, CompositorContextId); + +inline CompositorContextId allocate_compositor_context_id() +{ + static Atomic s_next_id { 1 }; + return CompositorContextId { s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed) }; +} + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 31614c9a90..7f5dc52c33 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -50,7 +50,6 @@ class DisplayList; class DisplayListPlayerSkia; class DisplayListRecorder; class DisplayListResourceStorage; -class ExternalContentSource; class VideoFrameSource; struct GradientPaintStyle; struct PatternPaintStyle; diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index 6481249423..4b5387b857 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,6 +54,7 @@ void HTMLCanvasElement::initialize(JS::Realm& realm) void HTMLCanvasElement::finalize() { + clear_compositor_surface(); Base::finalize(); document().page().unregister_canvas_element({}, unique_id()); } @@ -131,17 +132,16 @@ WebIDL::UnsignedLong HTMLCanvasElement::height() const return 150; } -Painting::ExternalContentSource& HTMLCanvasElement::ensure_external_content_source() +Painting::CompositorSurfaceId HTMLCanvasElement::ensure_compositor_surface_id() { - if (!m_external_content_source) - m_external_content_source = Painting::ExternalContentSource::create(); - return *m_external_content_source; + if (!m_compositor_surface_id.has_value()) + m_compositor_surface_id = Painting::allocate_compositor_surface_id(); + return *m_compositor_surface_id; } void HTMLCanvasElement::reset_context_to_default_state() { - if (m_external_content_source) - m_external_content_source->clear(); + clear_compositor_surface(); m_context.visit( [](GC::Ref& context) { context->reset_to_default_state(); @@ -453,11 +453,19 @@ void HTMLCanvasElement::present() if (auto surface = this->surface()) { surface->flush(); - auto snapshot = Gfx::DecodedImageFrame { *surface->snapshot_bitmap() }; - ensure_external_content_source().update(snapshot); + if (auto navigable = document().navigable()) + navigable->rendering_thread().update_compositor_surface(ensure_compositor_surface_id(), surface->snapshot_into_shared_image()); } } +void HTMLCanvasElement::clear_compositor_surface() +{ + if (!m_compositor_surface_id.has_value()) + return; + if (auto navigable = document().navigable()) + navigable->rendering_thread().clear_compositor_surface(*m_compositor_surface_id); +} + RefPtr HTMLCanvasElement::surface() const { return m_context.visit( diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Libraries/LibWeb/HTML/HTMLCanvasElement.h index b7df50e20a..63d614a12f 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.h +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -6,10 +6,11 @@ #pragma once +#include #include #include #include -#include +#include #include namespace Web::HTML { @@ -52,7 +53,7 @@ public: RefPtr surface() const; void allocate_painting_surface_if_needed(); - Painting::ExternalContentSource& ensure_external_content_source(); + Painting::CompositorSurfaceId ensure_compositor_surface_id(); CSS::ComputationContext canvas_font_computation_context(); @@ -73,9 +74,10 @@ private: JS::ThrowCompletionOr create_webgl_context(JS::Value options); void reset_context_to_default_state(); void notify_context_about_canvas_size_change(); + void clear_compositor_surface(); Variant, GC::Ref, GC::Ref, Empty> m_context; - RefPtr m_external_content_source; + Optional m_compositor_surface_id; bool m_canvas_content_dirty { false }; }; diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index d70fd5bfe2..ea544f2af4 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include @@ -297,12 +296,14 @@ Navigable::~Navigable() = default; void Navigable::set_has_been_destroyed() { + clear_compositor_surface(); m_has_been_destroyed = true; resolve_all_pending_async_scroll_operations(); } void Navigable::remove_from_all_navigables() { + clear_compositor_surface(); resolve_all_pending_async_scroll_operations(); if (m_active_document) @@ -312,6 +313,7 @@ void Navigable::remove_from_all_navigables() void Navigable::finalize() { + clear_compositor_surface(); all_navigables().remove(*this); Base::finalize(); } @@ -430,8 +432,11 @@ void Navigable::initialize_navigable(NonnullRefPtr document_state if (parent) m_should_show_line_box_borders = parent->m_should_show_line_box_borders; if (parent && !m_is_svg_page) { - m_external_content_source = Painting::ExternalContentSource::create(); - m_rendering_thread.set_presentation_mode(Compositor::CompositorThread::PublishToExternalContent { external_content_source() }); + m_compositor_surface_id = Painting::allocate_compositor_surface_id(); + m_rendering_thread.set_presentation_mode(Compositor::CompositorThread::PublishToCompositorSurface { + .target_context_id = parent->rendering_thread().context_id(), + .surface_id = *m_compositor_surface_id, + }); } // 6. Set the initial visibility state of documentState's document to navigable's traversable navigable's system visibility state. @@ -3274,10 +3279,19 @@ void Navigable::set_has_session_history_entry_and_ready_for_navigation() } } -NonnullRefPtr Navigable::external_content_source() const +Painting::CompositorSurfaceId Navigable::compositor_surface_id() const { - VERIFY(m_external_content_source); - return *m_external_content_source; + VERIFY(m_compositor_surface_id.has_value()); + return *m_compositor_surface_id; +} + +void Navigable::clear_compositor_surface() +{ + if (!m_compositor_surface_id.has_value()) + return; + if (auto parent = this->parent()) + parent->rendering_thread().clear_compositor_surface(*m_compositor_surface_id); + m_compositor_surface_id.clear(); } void Navigable::set_should_show_line_box_borders(bool value) @@ -3332,14 +3346,18 @@ void Navigable::record_display_list_and_scroll_state(PaintConfig paint_config) void Navigable::paint_next_frame() { + if (has_been_destroyed()) + return; + auto viewport_rect = page().css_to_device_rect(this->viewport_rect()).to_type(); PaintConfig paint_config { .paint_overlay = true, .should_show_line_box_borders = m_should_show_line_box_borders }; if (is_top_level_traversable()) { paint_config.canvas_fill_rect = Gfx::IntRect { {}, viewport_rect.size() }; } else { - // Nested navigables publish transparent bitmaps to their preconfigured ExternalContentSource instead of filling + // Nested navigables publish transparent bitmaps to their preconfigured compositor surface instead of filling // the canvas for the UI process. - VERIFY(m_external_content_source); + if (!m_compositor_surface_id.has_value()) + return; } auto should_defer_main_thread_present_for_async_scroll = [&] { diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index f21b061c53..0cb5d3701d 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -241,7 +241,8 @@ public: Compositor::CompositorThread& rendering_thread() { return m_rendering_thread; } - NonnullRefPtr external_content_source() const; + Painting::CompositorSurfaceId compositor_surface_id() const; + bool has_compositor_surface_id() const { return m_compositor_surface_id.has_value(); } void set_pending_set_browser_zoom_request(bool value) { m_pending_set_browser_zoom_request = value; } bool pending_set_browser_zoom_request() const { return m_pending_set_browser_zoom_request; } @@ -277,6 +278,7 @@ private: void reset_cursor_blink_cycle(); void scroll_offset_did_change(); + void clear_compositor_surface(); void inform_the_navigation_api_about_aborting_navigation(); void resolve_async_scroll_operation(Compositor::AsyncScrollOperationID); @@ -334,7 +336,7 @@ private: Painting::DisplayListResourceStorage m_display_list_resource_storage; Painting::DisplayListResourceSet m_rendering_thread_display_list_resources; Compositor::CompositorThread m_rendering_thread; - RefPtr m_external_content_source; + Optional m_compositor_surface_id; struct PendingAsyncScrollOperation { Compositor::AsyncScrollOperationID operation_id { 0 }; diff --git a/Libraries/LibWeb/Painting/CanvasPaintable.cpp b/Libraries/LibWeb/Painting/CanvasPaintable.cpp index 5a692effb5..82261a1b97 100644 --- a/Libraries/LibWeb/Painting/CanvasPaintable.cpp +++ b/Libraries/LibWeb/Painting/CanvasPaintable.cpp @@ -32,13 +32,13 @@ void CanvasPaintable::paint(DisplayListRecordingContext& context, PaintPhase pha ScopedCornerRadiusClip corner_clip { context, canvas_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) }; auto& canvas_element = as(*dom_node()); - if (canvas_element.surface()) { + if (auto surface = canvas_element.surface()) { auto canvas_int_rect = canvas_rect.to_type(); auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), - canvas_element.surface()->size(), canvas_int_rect.size()); + surface->size(), canvas_int_rect.size()); auto& mutable_canvas_element = const_cast(canvas_element); - context.display_list_recorder().draw_external_content(canvas_int_rect, - mutable_canvas_element.ensure_external_content_source(), scaling_mode); + context.display_list_recorder().draw_compositor_surface(canvas_int_rect, + mutable_canvas_element.ensure_compositor_surface_id(), scaling_mode); } } } diff --git a/Libraries/LibWeb/Painting/DisplayList.h b/Libraries/LibWeb/Painting/DisplayList.h index 853979bc0e..93e65faff4 100644 --- a/Libraries/LibWeb/Painting/DisplayList.h +++ b/Libraries/LibWeb/Painting/DisplayList.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -97,7 +96,7 @@ private: virtual void fill_rect(FillRect const&) = 0; virtual void draw_scaled_decoded_image_frame(DrawScaledDecodedImageFrame const&) = 0; virtual void draw_repeated_decoded_image_frame(DrawRepeatedDecodedImageFrame const&) = 0; - virtual void draw_external_content(DrawExternalContent const&) = 0; + virtual void draw_compositor_surface(DrawCompositorSurface const&) = 0; virtual void draw_video_frame_source(DrawVideoFrameSource const&) = 0; virtual void save(Save const&) = 0; virtual void save_layer(SaveLayer const&) = 0; diff --git a/Libraries/LibWeb/Painting/DisplayListCommand.cpp b/Libraries/LibWeb/Painting/DisplayListCommand.cpp index 4ab5a0d7ff..7c713abdec 100644 --- a/Libraries/LibWeb/Painting/DisplayListCommand.cpp +++ b/Libraries/LibWeb/Painting/DisplayListCommand.cpp @@ -40,7 +40,7 @@ void DrawRepeatedDecodedImageFrame::dump(StringBuilder& builder) const builder.appendff(" dst_rect={} clip_rect={}", dst_rect, clip_rect); } -void DrawExternalContent::dump(StringBuilder& builder) const +void DrawCompositorSurface::dump(StringBuilder& builder) const { builder.appendff(" dst_rect={}", dst_rect); } diff --git a/Libraries/LibWeb/Painting/DisplayListCommand.h b/Libraries/LibWeb/Painting/DisplayListCommand.h index d09e694060..5f3e59940e 100644 --- a/Libraries/LibWeb/Painting/DisplayListCommand.h +++ b/Libraries/LibWeb/Painting/DisplayListCommand.h @@ -38,7 +38,7 @@ class DisplayList; V(FillRect, fill_rect) \ V(DrawScaledDecodedImageFrame, draw_scaled_decoded_image_frame) \ V(DrawRepeatedDecodedImageFrame, draw_repeated_decoded_image_frame) \ - V(DrawExternalContent, draw_external_content) \ + V(DrawCompositorSurface, draw_compositor_surface) \ V(DrawVideoFrameSource, draw_video_frame_source) \ V(Save, save) \ V(SaveLayer, save_layer) \ @@ -165,12 +165,12 @@ struct DrawRepeatedDecodedImageFrame { void dump(StringBuilder&) const; }; -struct DrawExternalContent { - static constexpr StringView command_name = "DrawExternalContent"sv; - static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawExternalContent; +struct DrawCompositorSurface { + static constexpr StringView command_name = "DrawCompositorSurface"sv; + static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawCompositorSurface; Gfx::IntRect dst_rect; - ExternalContentResourceId source_id; + CompositorSurfaceId surface_id; Gfx::ScalingMode scaling_mode; [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index a30438a7ea..26057d0be8 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -165,14 +165,16 @@ void DisplayListPlayerSkia::fill_rect(FillRect const& command) canvas.drawRect(to_skia_rect(rect), paint); } -void DisplayListPlayerSkia::draw_external_content(DrawExternalContent const& command) +void DisplayListPlayerSkia::draw_compositor_surface(DrawCompositorSurface const& command) { - auto frame = resource_storage().external_content_source(command.source_id).current_frame(); + auto frame = resource_storage().compositor_surface(command.surface_id); if (!frame.has_value()) return; - auto image = m_image_cache.image_for_frame(*frame); + + auto image = m_image_cache.image_for_frame(frame.value()); if (!image) return; + auto dst_rect = to_skia_rect(command.dst_rect); SkRect src_rect = SkRect::MakeIWH(image->width(), image->height()); auto& canvas = surface().canvas(); diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index 1043cc384b..1e8accea78 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -28,7 +28,7 @@ private: void fill_rect(FillRect const&) override; void draw_scaled_decoded_image_frame(DrawScaledDecodedImageFrame const&) override; void draw_repeated_decoded_image_frame(DrawRepeatedDecodedImageFrame const&) override; - void draw_external_content(DrawExternalContent const&) override; + void draw_compositor_surface(DrawCompositorSurface const&) override; void draw_video_frame_source(DrawVideoFrameSource const&) override; void add_clip_rect(AddClipRect const&) override; void save(Save const&) override; diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index c825dffeac..b7839e1e5d 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -499,13 +499,13 @@ void DisplayListRecorder::draw_rect(Gfx::IntRect const& rect, Color color, bool .rough = rough }); } -void DisplayListRecorder::draw_external_content(Gfx::IntRect const& dst_rect, NonnullRefPtr source, Gfx::ScalingMode scaling_mode) +void DisplayListRecorder::draw_compositor_surface(Gfx::IntRect const& dst_rect, CompositorSurfaceId surface_id, Gfx::ScalingMode scaling_mode) { if (dst_rect.is_empty()) return; - append_command(DrawExternalContent { + append_command(DrawCompositorSurface { .dst_rect = dst_rect, - .source_id = resource_storage().add_external_content_source(move(source)), + .surface_id = surface_id, .scaling_mode = scaling_mode, }); } diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Libraries/LibWeb/Painting/DisplayListRecorder.h index 75bf021474..2273bafe52 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -72,7 +72,7 @@ public: void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false); void draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); - void draw_external_content(Gfx::IntRect const& dst_rect, NonnullRefPtr, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); + void draw_compositor_surface(Gfx::IntRect const& dst_rect, CompositorSurfaceId, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); void draw_video_frame_source(Gfx::IntRect const& dst_rect, NonnullRefPtr, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); void draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y); diff --git a/Libraries/LibWeb/Painting/DisplayListResourceIds.h b/Libraries/LibWeb/Painting/DisplayListResourceIds.h index 18f6ad80f8..6c45cc64da 100644 --- a/Libraries/LibWeb/Painting/DisplayListResourceIds.h +++ b/Libraries/LibWeb/Painting/DisplayListResourceIds.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include @@ -13,8 +14,14 @@ namespace Web::Painting { AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, FontResourceId); AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, ImageFrameResourceId); -AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, ExternalContentResourceId); AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, VideoFrameResourceId); AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, DisplayListResourceId); +AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, CompositorSurfaceId); + +inline CompositorSurfaceId allocate_compositor_surface_id() +{ + static Atomic s_next_id { 1 }; + return CompositorSurfaceId { s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed) }; +} } diff --git a/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp b/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp index 9102f998df..45af183fe0 100644 --- a/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp +++ b/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -27,14 +28,6 @@ ImageFrameResourceId DisplayListResourceStorage::add_image_frame(Gfx::DecodedIma return { id }; } -ExternalContentResourceId DisplayListResourceStorage::add_external_content_source( - NonnullRefPtr source) -{ - auto id = source->id(); - m_external_content_sources.ensure(id, [&] { return move(source); }); - return { id }; -} - VideoFrameResourceId DisplayListResourceStorage::add_video_frame_source(NonnullRefPtr source) { auto id = source->id(); @@ -64,8 +57,6 @@ void DisplayListResourceStorage::append_referenced_resources_from( add_font(source.font(id)); for (auto id : referenced_resources.image_frames) add_image_frame(source.image_frame(id)); - for (auto id : referenced_resources.external_content_sources) - add_external_content_source(source.external_content_source(id)); for (auto id : referenced_resources.video_frame_sources) add_video_frame_source(source.video_frame_source(id)); for (auto id : referenced_resources.display_lists) @@ -89,8 +80,6 @@ void DisplayListResourceStorage::collect_referenced_resources( referenced_resources.fonts.set(command.font_id, AK::HashSetExistingEntryBehavior::Keep); if constexpr (requires { command.frame_id; }) referenced_resources.image_frames.set(command.frame_id, AK::HashSetExistingEntryBehavior::Keep); - if constexpr (requires { external_content_source(command.source_id); }) - referenced_resources.external_content_sources.set(command.source_id, AK::HashSetExistingEntryBehavior::Keep); if constexpr (requires { video_frame_source(command.source_id); }) referenced_resources.video_frame_sources.set(command.source_id, AK::HashSetExistingEntryBehavior::Keep); if constexpr (requires { command.paint_style; command.paint_kind; }) { @@ -150,10 +139,6 @@ DisplayListResourceTransaction DisplayListResourceStorage::create_transaction( if (!previous.image_frames.contains(id)) transaction.image_frames.append(image_frame(id)); } - for (auto id : current.external_content_sources) { - if (!previous.external_content_sources.contains(id)) - transaction.external_content_sources.append(external_content_source(id)); - } for (auto id : current.video_frame_sources) { if (!previous.video_frame_sources.contains(id)) transaction.video_frame_sources.append(video_frame_source(id)); @@ -171,10 +156,6 @@ DisplayListResourceTransaction DisplayListResourceStorage::create_transaction( if (!current.image_frames.contains(id)) transaction.image_frame_ids_to_remove.append(id); } - for (auto id : previous.external_content_sources) { - if (!current.external_content_sources.contains(id)) - transaction.external_content_source_ids_to_remove.append(id); - } for (auto id : previous.video_frame_sources) { if (!current.video_frame_sources.contains(id)) transaction.video_frame_source_ids_to_remove.append(id); @@ -183,7 +164,6 @@ DisplayListResourceTransaction DisplayListResourceStorage::create_transaction( if (!current.display_lists.contains(id)) transaction.display_list_ids_to_remove.append(id); } - return transaction; } @@ -193,8 +173,6 @@ void DisplayListResourceStorage::apply_transaction(DisplayListResourceTransactio add_font(*font); for (auto const& frame : transaction.image_frames) add_image_frame(frame); - for (auto& source : transaction.external_content_sources) - add_external_content_source(move(source)); for (auto& source : transaction.video_frame_sources) add_video_frame_source(move(source)); for (auto& display_list : transaction.display_lists) @@ -204,8 +182,6 @@ void DisplayListResourceStorage::apply_transaction(DisplayListResourceTransactio m_fonts.remove(id.value()); for (auto id : transaction.image_frame_ids_to_remove) m_image_frames.remove(id.value()); - for (auto id : transaction.external_content_source_ids_to_remove) - m_external_content_sources.remove(id.value()); for (auto id : transaction.video_frame_source_ids_to_remove) m_video_frame_sources.remove(id.value()); for (auto id : transaction.display_list_ids_to_remove) @@ -216,9 +192,19 @@ void DisplayListResourceStorage::retain_only(DisplayListResourceSet const& resou { m_fonts.remove_all_matching([&](auto id, auto const&) { return !resource_set.fonts.contains(FontResourceId { id }); }); m_image_frames.remove_all_matching([&](auto id, auto const&) { return !resource_set.image_frames.contains(ImageFrameResourceId { id }); }); - m_external_content_sources.remove_all_matching([&](auto id, auto const&) { return !resource_set.external_content_sources.contains(ExternalContentResourceId { id }); }); m_video_frame_sources.remove_all_matching([&](auto id, auto const&) { return !resource_set.video_frame_sources.contains(VideoFrameResourceId { id }); }); m_display_lists.remove_all_matching([&](auto id, auto const&) { return !resource_set.display_lists.contains(DisplayListResourceId { id }); }); } +void DisplayListResourceStorage::update_compositor_surface(CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image) +{ + auto shared_image_buffer = Gfx::SharedImageBuffer::import_from_shared_image(move(shared_image)); + m_compositor_surfaces.set(surface_id.value(), Gfx::DecodedImageFrame { *shared_image_buffer.bitmap() }); +} + +void DisplayListResourceStorage::clear_compositor_surface(CompositorSurfaceId surface_id) +{ + m_compositor_surfaces.remove(surface_id.value()); +} + } diff --git a/Libraries/LibWeb/Painting/DisplayListResourceStorage.h b/Libraries/LibWeb/Painting/DisplayListResourceStorage.h index e2f03837c6..ee230a7cea 100644 --- a/Libraries/LibWeb/Painting/DisplayListResourceStorage.h +++ b/Libraries/LibWeb/Painting/DisplayListResourceStorage.h @@ -11,13 +11,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include namespace Web::Painting { @@ -25,7 +25,6 @@ namespace Web::Painting { struct DisplayListResourceSet { HashTable fonts; HashTable image_frames; - HashTable external_content_sources; HashTable video_frame_sources; HashTable display_lists; }; @@ -33,13 +32,11 @@ struct DisplayListResourceSet { struct DisplayListResourceTransaction { Vector> fonts; Vector image_frames; - Vector> external_content_sources; Vector> video_frame_sources; Vector> display_lists; Vector font_ids_to_remove; Vector image_frame_ids_to_remove; - Vector external_content_source_ids_to_remove; Vector video_frame_source_ids_to_remove; Vector display_list_ids_to_remove; }; @@ -54,7 +51,6 @@ public: FontResourceId add_font(Gfx::Font const&); ImageFrameResourceId add_image_frame(Gfx::DecodedImageFrame const&); - ExternalContentResourceId add_external_content_source(NonnullRefPtr); VideoFrameResourceId add_video_frame_source(NonnullRefPtr); DisplayListResourceId add_display_list(NonnullRefPtr); void append_referenced_resources_from(DisplayListResourceStorage const& source, ReadonlyBytes command_bytes); @@ -63,21 +59,23 @@ public: DisplayListResourceSet collect_referenced_resources(DisplayList const&) const; DisplayListResourceSet collect_referenced_resources(ReadonlyBytes command_bytes) const; void retain_only(DisplayListResourceSet const&); + void update_compositor_surface(CompositorSurfaceId, Gfx::SharedImage&&); + void clear_compositor_surface(CompositorSurfaceId); Gfx::Font const& font(FontResourceId id) const { return *m_fonts.get(id.value()).value(); } Gfx::DecodedImageFrame const& image_frame(ImageFrameResourceId id) const { return m_image_frames.get(id.value()).value(); } - ExternalContentSource const& external_content_source(ExternalContentResourceId id) const { return *m_external_content_sources.get(id.value()).value(); } VideoFrameSource const& video_frame_source(VideoFrameResourceId id) const { return *m_video_frame_sources.get(id.value()).value(); } DisplayList const& display_list(DisplayListResourceId id) const { return *m_display_lists.get(id.value()).value(); } + Optional compositor_surface(CompositorSurfaceId id) const { return m_compositor_surfaces.get(id.value()); } private: void collect_referenced_resources(ReadonlyBytes command_bytes, DisplayListResourceSet&) const; HashMap> m_fonts; HashMap m_image_frames; - HashMap> m_external_content_sources; HashMap> m_video_frame_sources; HashMap> m_display_lists; + HashMap m_compositor_surfaces; }; } diff --git a/Libraries/LibWeb/Painting/ExternalContentSource.cpp b/Libraries/LibWeb/Painting/ExternalContentSource.cpp deleted file mode 100644 index ae8dacf4c4..0000000000 --- a/Libraries/LibWeb/Painting/ExternalContentSource.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2026, Aliaksandr Kalenik - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Web::Painting { - -static Atomic s_next_id { 1 }; - -NonnullRefPtr ExternalContentSource::create() -{ - return adopt_ref(*new ExternalContentSource()); -} - -ExternalContentSource::ExternalContentSource() - : m_id(s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed)) -{ -} - -void ExternalContentSource::update(Optional frame) -{ - Optional old; - { - Sync::MutexLocker const locker { m_mutex }; - old = move(m_frame); - m_frame = move(frame); - } -} - -void ExternalContentSource::clear() -{ - Optional old; - { - Sync::MutexLocker const locker { m_mutex }; - old = move(m_frame); - } -} - -Optional ExternalContentSource::current_frame() const -{ - Sync::MutexLocker const locker { m_mutex }; - return m_frame; -} - -} diff --git a/Libraries/LibWeb/Painting/ExternalContentSource.h b/Libraries/LibWeb/Painting/ExternalContentSource.h deleted file mode 100644 index 60453a91ac..0000000000 --- a/Libraries/LibWeb/Painting/ExternalContentSource.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2026, Aliaksandr Kalenik - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Web::Painting { - -class ExternalContentSource final : public AtomicRefCounted { -public: - static NonnullRefPtr create(); - - u64 id() const { return m_id; } - - void update(Optional); - void clear(); - Optional current_frame() const; - -private: - ExternalContentSource(); - - u64 m_id { 0 }; - mutable Sync::Mutex m_mutex; - Optional m_frame; -}; - -} diff --git a/Libraries/LibWeb/Painting/NavigableContainerViewportPaintable.cpp b/Libraries/LibWeb/Painting/NavigableContainerViewportPaintable.cpp index 6bff780797..813e44d286 100644 --- a/Libraries/LibWeb/Painting/NavigableContainerViewportPaintable.cpp +++ b/Libraries/LibWeb/Painting/NavigableContainerViewportPaintable.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include namespace Web::Painting { @@ -48,12 +47,14 @@ void NavigableContainerViewportPaintable::paint(DisplayListRecordingContext& con auto content_navigable = navigable_container.content_navigable(); VERIFY(content_navigable); + if (content_navigable->has_been_destroyed() || !content_navigable->has_compositor_surface_id()) + return; context.display_list_recorder().save(); context.display_list_recorder().add_clip_rect(clip_rect.to_type()); - context.display_list_recorder().draw_external_content( + context.display_list_recorder().draw_compositor_surface( context.enclosing_device_rect(absolute_rect).to_type(), - content_navigable->external_content_source(), + content_navigable->compositor_surface_id(), Gfx::ScalingMode::NearestNeighbor); context.display_list_recorder().restore(); diff --git a/Tests/LibWeb/Text/expected/async-scrolling/nested-navigable-wheel-admission.txt b/Tests/LibWeb/Text/expected/async-scrolling/nested-navigable-wheel-admission.txt index 8de0bcd16b..88f5227da2 100644 --- a/Tests/LibWeb/Text/expected/async-scrolling/nested-navigable-wheel-admission.txt +++ b/Tests/LibWeb/Text/expected/async-scrolling/nested-navigable-wheel-admission.txt @@ -16,6 +16,6 @@ SaveLayer@0 CompositorMainThreadWheelEventRegion@1 rect=[40,40 160x120] Save@1 AddClipRect@1 rect=[40,40 160x120] - DrawExternalContent@1 dst_rect=[40,40 160x120] + DrawCompositorSurface@1 dst_rect=[40,40 160x120] Restore@1 Restore@0 diff --git a/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt b/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt new file mode 100644 index 0000000000..9ab662a64b --- /dev/null +++ b/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt @@ -0,0 +1,12 @@ +AccumulatedVisualContext Tree: + [1] scroll_frame_id=1 (PaintableWithLines(BlockContainer
#out))
+
+DisplayList:
+SaveLayer@0
+  CompositorWheelHitTestTarget@0 target_scroll_frame_index=0 rect=[0,0 800x600]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[0,0 800x53]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 784x32]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 784x32]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 32x32]
+  DrawCompositorSurface@1 dst_rect=[8,8 32x32]
+Restore@0
diff --git a/Tests/LibWeb/Text/expected/display_list/iframe-compositor-surface.txt b/Tests/LibWeb/Text/expected/display_list/iframe-compositor-surface.txt
new file mode 100644
index 0000000000..21208fc475
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/display_list/iframe-compositor-surface.txt
@@ -0,0 +1,16 @@
+AccumulatedVisualContext Tree:
+  [1] scroll_frame_id=1 (PaintableWithLines(BlockContainer
#out))
+
+DisplayList:
+SaveLayer@0
+  CompositorWheelHitTestTarget@0 target_scroll_frame_index=0 rect=[0,0 800x600]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[0,0 800x93]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[0,0 800x80]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[0,0 800x80]
+  CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[0,0 120x80]
+  CompositorMainThreadWheelEventRegion@1 rect=[0,0 120x80]
+  Save@1
+    AddClipRect@1 rect=[0,0 120x80]
+    DrawCompositorSurface@1 dst_rect=[0,0 120x80]
+  Restore@1
+Restore@0
diff --git a/Tests/LibWeb/Text/input/display_list/canvas-compositor-surface.html b/Tests/LibWeb/Text/input/display_list/canvas-compositor-surface.html
new file mode 100644
index 0000000000..ead0e7e858
--- /dev/null
+++ b/Tests/LibWeb/Text/input/display_list/canvas-compositor-surface.html
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/display_list/iframe-compositor-surface.html b/Tests/LibWeb/Text/input/display_list/iframe-compositor-surface.html
new file mode 100644
index 0000000000..9fb50c2704
--- /dev/null
+++ b/Tests/LibWeb/Text/input/display_list/iframe-compositor-surface.html
@@ -0,0 +1,28 @@
+
+
+
+
+