LibWeb+Compositor: Add canvas display-list surfaces
Canvas contexts need a compositor-owned surface path that can be shared by 2D canvas and WebGL. Add CanvasId and a CanvasSurfaceRegistry, pass the registry into display-list playback, and teach Skia playback how to resolve and draw a registered canvas surface. This only adds the shared display-list command and registry plumbing. Existing canvas elements still publish their old compositor surfaces, so the behavior change is left for the later canvas-host commits.
This commit is contained in:
parent
7dadfa2e52
commit
af8b41e1cb
14 changed files with 132 additions and 6 deletions
|
|
@ -46,6 +46,7 @@ namespace Web::Painting {
|
|||
class AccumulatedVisualContextTree;
|
||||
class BackingStore;
|
||||
class ChromeWidget;
|
||||
class CanvasSurfaceRegistry;
|
||||
class DevicePixelConverter;
|
||||
class DisplayList;
|
||||
class DisplayListPlayerSkia;
|
||||
|
|
|
|||
57
Libraries/LibWeb/Painting/CanvasSurfaceRegistry.h
Normal file
57
Libraries/LibWeb/Painting/CanvasSurfaceRegistry.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibWeb/Painting/DisplayListResourceIds.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
class CanvasSurfaceRegistry {
|
||||
AK_MAKE_NONCOPYABLE(CanvasSurfaceRegistry);
|
||||
AK_MAKE_DEFAULT_MOVABLE(CanvasSurfaceRegistry);
|
||||
|
||||
public:
|
||||
CanvasSurfaceRegistry() = default;
|
||||
~CanvasSurfaceRegistry() = default;
|
||||
|
||||
CanvasId allocate_canvas_id()
|
||||
{
|
||||
return CanvasId { m_next_canvas_id++ };
|
||||
}
|
||||
|
||||
CanvasId create_canvas_surface(NonnullRefPtr<Gfx::PaintingSurface> surface)
|
||||
{
|
||||
auto id = allocate_canvas_id();
|
||||
set_canvas_surface(id, move(surface));
|
||||
return id;
|
||||
}
|
||||
|
||||
void set_canvas_surface(CanvasId id, NonnullRefPtr<Gfx::PaintingSurface> surface)
|
||||
{
|
||||
m_surfaces.set(id, move(surface));
|
||||
}
|
||||
|
||||
void remove_canvas_surface(CanvasId id)
|
||||
{
|
||||
m_surfaces.remove(id);
|
||||
}
|
||||
|
||||
Gfx::PaintingSurface const* canvas_surface(CanvasId id) const
|
||||
{
|
||||
return m_surfaces.get(id).value_or(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_next_canvas_id { 1 };
|
||||
HashMap<CanvasId, NonnullRefPtr<Gfx::PaintingSurface>> m_surfaces;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -110,14 +110,17 @@ void DisplayListPlayer::execute(
|
|||
AccumulatedVisualContextTree const& visual_context_tree,
|
||||
DisplayListResourceStorage const& resource_storage,
|
||||
ScrollStateSnapshot const& scroll_state_snapshot,
|
||||
RefPtr<Gfx::PaintingSurface> surface)
|
||||
RefPtr<Gfx::PaintingSurface> surface,
|
||||
CanvasSurfaceRegistry const* canvas_surface_registry)
|
||||
{
|
||||
VERIFY(display_list.compatible_visual_context_tree_version() == visual_context_tree.version());
|
||||
m_surface = surface;
|
||||
m_active_display_list = &display_list;
|
||||
m_active_visual_context_tree = &visual_context_tree;
|
||||
m_resource_storage = &resource_storage;
|
||||
m_canvas_surface_registry = canvas_surface_registry;
|
||||
execute_impl(display_list, scroll_state_snapshot);
|
||||
m_canvas_surface_registry = nullptr;
|
||||
m_resource_storage = nullptr;
|
||||
m_active_visual_context_tree = nullptr;
|
||||
m_active_display_list = nullptr;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class WEB_API DisplayListPlayer {
|
|||
public:
|
||||
virtual ~DisplayListPlayer() = default;
|
||||
|
||||
void execute(DisplayList const&, AccumulatedVisualContextTree const&, DisplayListResourceStorage const&, ScrollStateSnapshot const&, RefPtr<Gfx::PaintingSurface>);
|
||||
void execute(DisplayList const&, AccumulatedVisualContextTree const&, DisplayListResourceStorage const&, ScrollStateSnapshot const&, RefPtr<Gfx::PaintingSurface>, CanvasSurfaceRegistry const* = nullptr);
|
||||
virtual void flush(Gfx::PaintingSurface&) = 0;
|
||||
|
||||
protected:
|
||||
|
|
@ -41,6 +41,7 @@ protected:
|
|||
DisplayList const& active_display_list() const { return *m_active_display_list; }
|
||||
AccumulatedVisualContextTree const& active_visual_context_tree() const { return *m_active_visual_context_tree; }
|
||||
DisplayListResourceStorage const& resource_storage() const { return *m_resource_storage; }
|
||||
CanvasSurfaceRegistry const* canvas_surface_registry() const { return m_canvas_surface_registry; }
|
||||
ReadonlyBytes inline_data(DisplayListDataSpan span) const
|
||||
{
|
||||
VERIFY(static_cast<size_t>(span.offset) + span.size <= m_current_command_payload.size());
|
||||
|
|
@ -73,6 +74,7 @@ private:
|
|||
DisplayList const* m_active_display_list { nullptr };
|
||||
AccumulatedVisualContextTree const* m_active_visual_context_tree { nullptr };
|
||||
DisplayListResourceStorage const* m_resource_storage { nullptr };
|
||||
CanvasSurfaceRegistry const* m_canvas_surface_registry { nullptr };
|
||||
RefPtr<Gfx::PaintingSurface> m_surface;
|
||||
ReadonlyBytes m_current_command_payload;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ void DrawCompositorSurface::dump(StringBuilder& builder) const
|
|||
builder.appendff(" dst_rect={}", dst_rect);
|
||||
}
|
||||
|
||||
void DrawCanvas::dump(StringBuilder& builder) const
|
||||
{
|
||||
builder.appendff(" dst_rect={}", dst_rect);
|
||||
}
|
||||
|
||||
void DrawVideoFrame::dump(StringBuilder& builder) const
|
||||
{
|
||||
builder.appendff(" dst_rect={}", dst_rect);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class DisplayList;
|
|||
V(DrawScaledDecodedImageFrame, draw_scaled_decoded_image_frame) \
|
||||
V(DrawRepeatedDecodedImageFrame, draw_repeated_decoded_image_frame) \
|
||||
V(DrawCompositorSurface, draw_compositor_surface) \
|
||||
V(DrawCanvas, draw_canvas) \
|
||||
V(DrawVideoFrame, draw_video_frame) \
|
||||
V(Save, save) \
|
||||
V(SaveLayer, save_layer) \
|
||||
|
|
@ -199,6 +200,18 @@ struct DrawCompositorSurface {
|
|||
void dump(StringBuilder&) const;
|
||||
};
|
||||
|
||||
struct DrawCanvas {
|
||||
static constexpr StringView command_name = "DrawCanvas"sv;
|
||||
static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawCanvas;
|
||||
|
||||
Gfx::IntRect dst_rect;
|
||||
CanvasId canvas_id;
|
||||
Gfx::ScalingMode scaling_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
|
||||
void dump(StringBuilder&) const;
|
||||
};
|
||||
|
||||
struct DrawVideoFrame {
|
||||
static constexpr StringView command_name = "DrawVideoFrame"sv;
|
||||
static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawVideoFrame;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <LibGfx/SkiaUtils.h>
|
||||
#include <LibGfx/YUVData.h>
|
||||
#include <LibMedia/VideoFrame.h>
|
||||
#include <LibWeb/Painting/CanvasSurfaceRegistry.h>
|
||||
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
|
@ -225,6 +226,28 @@ void DisplayListPlayerSkia::play_command(DrawCompositorSurface const& command)
|
|||
canvas.drawImageRect(image.get(), src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint);
|
||||
}
|
||||
|
||||
void DisplayListPlayerSkia::play_command(DrawCanvas const& command)
|
||||
{
|
||||
auto const* registry = canvas_surface_registry();
|
||||
if (!registry)
|
||||
return;
|
||||
|
||||
auto* canvas_surface = registry->canvas_surface(command.canvas_id);
|
||||
if (!canvas_surface)
|
||||
return;
|
||||
|
||||
auto image = canvas_surface->sk_image_snapshot<sk_sp<SkImage>>();
|
||||
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();
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawImageRect(image.get(), src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint);
|
||||
}
|
||||
|
||||
void DisplayListPlayerSkia::play_command(DrawVideoFrame const& command)
|
||||
{
|
||||
auto frame = resource_storage().video_frame(command.video_frame_id);
|
||||
|
|
|
|||
|
|
@ -523,6 +523,17 @@ void DisplayListRecorder::draw_compositor_surface(Gfx::IntRect const& dst_rect,
|
|||
});
|
||||
}
|
||||
|
||||
void DisplayListRecorder::draw_canvas(Gfx::IntRect const& dst_rect, CanvasId canvas_id, Gfx::ScalingMode scaling_mode)
|
||||
{
|
||||
if (dst_rect.is_empty())
|
||||
return;
|
||||
append_command(DrawCanvas {
|
||||
.dst_rect = dst_rect,
|
||||
.canvas_id = canvas_id,
|
||||
.scaling_mode = scaling_mode,
|
||||
});
|
||||
}
|
||||
|
||||
void DisplayListRecorder::draw_video_frame(Gfx::IntRect const& dst_rect, VideoFrameResourceId frame_id, RefPtr<Media::VideoFrame const> frame, Gfx::ScalingMode scaling_mode)
|
||||
{
|
||||
if (dst_rect.is_empty())
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public:
|
|||
|
||||
void draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::DecodedImageFrame frame, 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_canvas(Gfx::IntRect const& dst_rect, CanvasId, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
|
||||
void draw_video_frame(Gfx::IntRect const& dst_rect, VideoFrameResourceId, RefPtr<Media::VideoFrame const>, 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);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, ImageFrameResourceId);
|
|||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, VideoFrameResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, DisplayListResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, CompositorSurfaceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, CanvasId);
|
||||
|
||||
inline VideoFrameResourceId allocate_video_frame_resource_id()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ void CompositorState::create_context(Web::Compositor::CompositorContextId contex
|
|||
VERIFY(context_id == Web::Compositor::compositor_context_id_for_page(*page_id));
|
||||
|
||||
auto& context = *m_contexts.ensure(context_id, [&] {
|
||||
return make<ContextState>(page_id, web_content_client, m_async_scrolling_enabled);
|
||||
return make<ContextState>(page_id, web_content_client, m_canvas_surface_registry, m_async_scrolling_enabled);
|
||||
});
|
||||
resize_backing_stores_if_needed(context_id, context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <LibWeb/Compositor/Types.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Painting/AccumulatedVisualContext.h>
|
||||
#include <LibWeb/Painting/CanvasSurfaceRegistry.h>
|
||||
#include <LibWeb/Painting/DisplayList.h>
|
||||
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
|
||||
#include <LibWeb/Painting/ScrollState.h>
|
||||
|
|
@ -69,6 +70,10 @@ public:
|
|||
ContextOwnerCheckResult check_context_owner(Web::Compositor::CompositorContextId, CompositorStateWebContentClient&);
|
||||
void destroy_contexts_for_web_content_client(CompositorStateWebContentClient&);
|
||||
|
||||
RefPtr<Gfx::SkiaBackendContext> skia_backend_context() const { return m_skia_backend_context; }
|
||||
Web::Painting::CanvasSurfaceRegistry& canvas_surface_registry() { return m_canvas_surface_registry; }
|
||||
Web::Painting::CanvasSurfaceRegistry const& canvas_surface_registry() const { return m_canvas_surface_registry; }
|
||||
|
||||
void create_context(Web::Compositor::CompositorContextId, Optional<u64> page_id, CompositorStateWebContentClient&);
|
||||
void destroy_context(Web::Compositor::CompositorContextId);
|
||||
|
||||
|
|
@ -143,6 +148,7 @@ private:
|
|||
HashMap<Web::Compositor::CompositorContextId, OwnPtr<ContextState>> m_contexts;
|
||||
DoublyLinkedList<PendingAsyncPresent> m_pending_async_presents;
|
||||
RefPtr<Gfx::SkiaBackendContext> m_skia_backend_context;
|
||||
Web::Painting::CanvasSurfaceRegistry m_canvas_surface_registry;
|
||||
OwnPtr<Web::Painting::DisplayListPlayerSkia> m_display_list_player;
|
||||
HashMap<Optional<u64>, OwnPtr<VSyncScheduler>> m_vsync_schedulers_by_display;
|
||||
RefPtr<Core::Timer> m_gpu_completion_timer;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <Compositor/CompositorState.h>
|
||||
#include <Compositor/ContextState.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/PainterSkia.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
|
|
@ -79,8 +80,9 @@ static void clamp_visual_viewport_transform_to_viewport(Web::Painting::Transform
|
|||
transform.matrix[1, 3] = clamp(transform.matrix[1, 3], min_y, 0.0f);
|
||||
}
|
||||
|
||||
ContextState::ContextState(Optional<u64> page_id, CompositorStateWebContentClient& web_content_client, bool async_scrolling_enabled)
|
||||
ContextState::ContextState(Optional<u64> page_id, CompositorStateWebContentClient& web_content_client, Web::Painting::CanvasSurfaceRegistry const& canvas_surface_registry, bool async_scrolling_enabled)
|
||||
: m_web_content_client(web_content_client)
|
||||
, m_canvas_surface_registry(canvas_surface_registry)
|
||||
, m_page_id(page_id)
|
||||
, m_async_scrolling_enabled(async_scrolling_enabled)
|
||||
{
|
||||
|
|
@ -877,7 +879,7 @@ Web::Painting::AccumulatedVisualContextTree const& ContextState::visual_context_
|
|||
void ContextState::paint_current_display_list(Web::Painting::DisplayListPlayerSkia& display_list_player, Gfx::PaintingSurface& surface)
|
||||
{
|
||||
VERIFY(m_display_list);
|
||||
display_list_player.execute(*m_display_list, visual_context_tree_for_compositing(), m_display_list_resource_storage, m_scroll_state_snapshot, surface);
|
||||
display_list_player.execute(*m_display_list, visual_context_tree_for_compositing(), m_display_list_resource_storage, m_scroll_state_snapshot, surface, &m_canvas_surface_registry);
|
||||
m_viewport_scrollbar_controller.paint(surface, display_list_player, m_scroll_state_snapshot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public:
|
|||
i32 bitmap_id { 0 };
|
||||
};
|
||||
|
||||
ContextState(Optional<u64> page_id, CompositorStateWebContentClient&, bool async_scrolling_enabled);
|
||||
ContextState(Optional<u64> page_id, CompositorStateWebContentClient&, Web::Painting::CanvasSurfaceRegistry const&, bool async_scrolling_enabled);
|
||||
~ContextState();
|
||||
|
||||
static bool presentation_mode_presents_to_client(Web::Compositor::PresentationMode const&);
|
||||
|
|
@ -179,6 +179,7 @@ private:
|
|||
void paint_current_display_list(Web::Painting::DisplayListPlayerSkia&, Gfx::PaintingSurface&);
|
||||
|
||||
CompositorStateWebContentClient& m_web_content_client;
|
||||
Web::Painting::CanvasSurfaceRegistry const& m_canvas_surface_registry;
|
||||
Optional<u64> m_page_id;
|
||||
bool const m_async_scrolling_enabled { true };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue