ladybird/Services/WebContent/WebContentCompositorHost.cpp
Aliaksandr Kalenik 5f0e95de13 Meta+LibWeb+Compositor: Add remote canvas transports
The display list can now refer to canvas ids, but WebContent still had
no channel for creating or updating those canvas resources in the
Compositor. Both 2D and WebGL canvases would have had to grow the IPC
plumbing in the same commit that changes the rendering contexts.

This adds the Compositor-side CanvasHost, WebContent transport objects,
and the IPC/CMake pieces needed to allocate, update, read back, and
destroy remote canvas contexts. The rendering contexts are not switched
over yet, keeping this as plumbing for later commits.
2026-06-17 19:07:32 +02:00

252 lines
11 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/NonnullOwnPtr.h>
#include <AK/Optional.h>
#include <LibGfx/CanvasCommandList.h>
#include <LibGfx/PaintingSurface.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Compositor/CompositorHost.h>
#include <LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h>
#include <LibWeb/WebGL/RemoteWebGLTransport.h>
#include <WebContent/CompositorConnection.h>
#include <WebContent/ConnectionFromClient.h>
#include <WebContent/WebContentCompositorHost.h>
namespace WebContent {
class WebContentRemoteWebGLTransport final : public Web::WebGL::RemoteWebGLTransport {
public:
explicit WebContentRemoteWebGLTransport(NonnullRefPtr<CompositorConnection> connection)
: m_connection(move(connection))
{
}
private:
virtual CreateResult create_context(Web::WebGL::WebGLVersion webgl_version, Gfx::IntSize initial_size, bool depth, bool stencil, bool antialias) override
{
CreateResult result;
auto canvas_id = m_connection->create_webgl_context(webgl_version, initial_size, depth, stencil, antialias, result.supported_extensions);
if (canvas_id.has_value()) {
result.success = true;
result.canvas_id = *canvas_id;
}
return result;
}
virtual void destroy_context(Web::Painting::CanvasId canvas_id) override
{
m_connection->destroy_canvas_context(canvas_id);
}
virtual void send_commands(Web::Painting::CanvasId canvas_id, ByteBuffer const& commands, Vector<Gfx::DecodedImageFrame> const& bitmaps) override
{
m_connection->send_webgl_commands(canvas_id, commands, bitmaps);
}
virtual void present_canvas(Web::Painting::CanvasId canvas_id, bool preserve_drawing_buffer) override
{
m_connection->present_webgl_canvas(canvas_id, preserve_drawing_buffer);
}
virtual ByteBuffer sync_call(Web::Painting::CanvasId canvas_id, ByteBuffer request) override
{
return m_connection->webgl_sync_call(canvas_id, move(request));
}
virtual Web::WebGL::ReadPixelsResult read_pixels_robust_angle(Web::Painting::CanvasId canvas_id, Web::WebGL::GLint x, Web::WebGL::GLint y, Web::WebGL::GLsizei width, Web::WebGL::GLsizei height, Web::WebGL::GLenum format, Web::WebGL::GLenum type, Web::WebGL::GLsizei buf_size, Core::AnonymousBuffer pixels) override
{
return m_connection->read_webgl_pixels(canvas_id, x, y, width, height, format, type, buf_size, pixels);
}
virtual void read_buffer_sub_data(Web::Painting::CanvasId canvas_id, Web::WebGL::GLenum target, Web::WebGL::GLintptr offset, Web::WebGL::GLintptr size, Core::AnonymousBuffer data) override
{
m_connection->read_webgl_buffer_sub_data(canvas_id, target, offset, size, data);
}
virtual Gfx::ShareableBitmap read_back_drawing_buffer(Web::Painting::CanvasId canvas_id, Gfx::IntRect const& rect) override
{
return m_connection->get_canvas_pixels(canvas_id, rect);
}
NonnullRefPtr<CompositorConnection> m_connection;
};
class WebContentRemoteCanvas2DTransport final : public Web::HTML::RemoteCanvas2DTransport {
public:
explicit WebContentRemoteCanvas2DTransport(NonnullRefPtr<CompositorConnection> connection)
: m_connection(move(connection))
{
}
private:
virtual Optional<Web::Painting::CanvasId> create_context(Gfx::IntSize size, bool alpha) override
{
return m_connection->create_canvas_2d_context(size, alpha);
}
virtual void destroy_context(Web::Painting::CanvasId canvas_id) override
{
m_connection->destroy_canvas_context(canvas_id);
}
virtual void update_commands(Web::Painting::CanvasId canvas_id, Gfx::CanvasCommandList const& commands) override
{
m_connection->update_canvas_2d_commands(canvas_id, commands);
}
virtual RefPtr<Gfx::Bitmap> read_back_pixels(Web::Painting::CanvasId canvas_id, Gfx::IntRect const& rect) override
{
auto shareable_bitmap = m_connection->get_canvas_pixels(canvas_id, rect);
if (!shareable_bitmap.is_valid())
return nullptr;
return shareable_bitmap.bitmap();
}
NonnullRefPtr<CompositorConnection> m_connection;
};
class WebContentCompositorHost final : public Web::Compositor::CompositorHost {
public:
explicit WebContentCompositorHost(ConnectionFromClient& client)
: m_client(client)
{
}
private:
virtual RefPtr<Web::WebGL::RemoteWebGLTransport> create_webgl_transport() override
{
if (auto* connection = compositor_connection())
return adopt_ref(*new WebContentRemoteWebGLTransport(*connection));
return nullptr;
}
virtual RefPtr<Web::HTML::RemoteCanvas2DTransport> create_canvas_2d_transport() override
{
if (auto* connection = compositor_connection())
return adopt_ref(*new WebContentRemoteCanvas2DTransport(*connection));
return nullptr;
}
virtual void destroy_context(Web::Compositor::CompositorContextId context_id) override
{
if (auto* connection = compositor_connection())
connection->destroy_context(context_id);
m_client.did_destroy_compositor_context(context_id);
}
virtual void set_presentation_mode(Web::Compositor::CompositorContextId context_id, Web::Compositor::PresentationMode mode) override
{
if (auto* connection = compositor_connection())
connection->set_presentation_mode(context_id, mode);
}
virtual void update_display_list(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Web::Painting::DisplayList> display_list, Web::Painting::AccumulatedVisualContextTree visual_context_tree, Web::Painting::DisplayListResourceTransaction&& resource_transaction, Web::Painting::ScrollStateSnapshot&& scroll_state_snapshot) override
{
if (auto* connection = compositor_connection())
connection->update_display_list(context_id, display_list, visual_context_tree, resource_transaction, scroll_state_snapshot);
}
virtual void update_visual_context_tree(Web::Compositor::CompositorContextId context_id, Web::Painting::AccumulatedVisualContextTree visual_context_tree) override
{
if (auto* connection = compositor_connection())
connection->update_visual_context_tree(context_id, visual_context_tree);
}
virtual void update_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame) override
{
if (auto* connection = compositor_connection())
connection->update_video_frame(context_id, frame_id, frame);
}
virtual void clear_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id) override
{
if (auto* connection = compositor_connection())
connection->clear_video_frame(context_id, frame_id);
}
virtual void update_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image) override
{
if (auto* connection = compositor_connection())
connection->update_compositor_surface(context_id, surface_id, shared_image);
}
virtual void clear_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id) override
{
if (auto* connection = compositor_connection())
connection->clear_compositor_surface(context_id, surface_id);
}
virtual void update_scroll_state(Web::Compositor::CompositorContextId context_id, Web::Painting::ScrollStateSnapshot&& scroll_state_snapshot) override
{
if (auto* connection = compositor_connection())
connection->update_scroll_state(context_id, scroll_state_snapshot);
}
virtual void invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId context_id, u64 generation) override
{
if (auto* connection = compositor_connection())
connection->invalidate_wheel_event_listener_state(context_id, generation);
}
virtual Web::Compositor::AsyncScrollEnqueueResult async_scroll_by(Web::Compositor::CompositorContextId context_id, Web::UniqueNodeID expected_document_id, Gfx::FloatPoint position,
Gfx::FloatPoint delta_in_device_pixels, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking operation_tracking) override
{
if (auto* connection = compositor_connection())
return connection->async_scroll_by(context_id, expected_document_id, position, delta_in_device_pixels, viewport_rect, operation_tracking);
return {};
}
virtual bool should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId context_id) const override
{
if (auto* connection = compositor_connection())
return connection->should_defer_main_thread_present_for_async_scroll(context_id);
return false;
}
virtual Web::Compositor::PendingAsyncScrollUpdates take_pending_async_scroll_updates(Web::Compositor::CompositorContextId context_id) override
{
if (auto* connection = compositor_connection())
return connection->take_pending_async_scroll_updates(context_id);
return {};
}
virtual void viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, Web::Compositor::WindowResizingInProgress window_resize_in_progress) override
{
if (auto* connection = compositor_connection())
connection->viewport_size_updated(context_id, viewport_size, window_resize_in_progress);
}
virtual void present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect viewport_rect) override
{
if (auto* connection = compositor_connection())
connection->present_frame(context_id, viewport_rect);
}
virtual void request_screenshot(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Gfx::PaintingSurface> target_surface, Function<void()>&& callback) override
{
if (auto* connection = compositor_connection()) {
connection->request_screenshot(context_id, move(target_surface), move(callback));
return;
}
if (callback)
callback();
}
CompositorConnection* compositor_connection() const
{
return m_client.compositor_process_connection();
}
ConnectionFromClient& m_client;
};
NonnullOwnPtr<Web::Compositor::CompositorHost> create_web_content_compositor_host(ConnectionFromClient& client)
{
return make<WebContentCompositorHost>(client);
}
}