Skip to content

Commit

Permalink
LibWeb+WebContent: Execute painting commands in a separate thread
Browse files Browse the repository at this point in the history
With this change, we take advantage of having a serialized list of
painting commands that does not have any pointers to
DOM/Layout/Paintable tree nodes. Now, when the recorded list of
commands is ready, instead of executing it on the main thread, we
schedule a task on the rendering thread and let the main thread proceed
to work on the next frame.

On my computer with CPU painter on
https://benchmarks.slaylines.io/dom.html we have 16 fps before and
38 fps after this change.
  • Loading branch information
kalenikaliaksandr committed Feb 9, 2024
1 parent 5926164 commit 9f54dc9
Show file tree
Hide file tree
Showing 14 changed files with 257 additions and 81 deletions.
3 changes: 2 additions & 1 deletion Ladybird/WebContent/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(WEBCONTENT_SOURCE_DIR ${SERENITY_SOURCE_DIR}/Userland/Services/WebContent/)
set(WEBCONTENT_SOURCES
${WEBCONTENT_SOURCE_DIR}/ConnectionFromClient.cpp
${WEBCONTENT_SOURCE_DIR}/ConsoleGlobalEnvironmentExtensions.cpp
${WEBCONTENT_SOURCE_DIR}/RenderLoopExecutor.cpp
${WEBCONTENT_SOURCE_DIR}/PageClient.cpp
${WEBCONTENT_SOURCE_DIR}/PageHost.cpp
${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp
Expand Down Expand Up @@ -80,7 +81,7 @@ endif()

target_include_directories(WebContent PRIVATE ${SERENITY_SOURCE_DIR}/Userland/Services/)
target_include_directories(WebContent PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(WebContent PRIVATE LibAudio LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibJS LibMain LibWeb LibWebSocket LibProtocol LibWebView)
target_link_libraries(WebContent PRIVATE LibAudio LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibJS LibMain LibWeb LibWebSocket LibProtocol LibWebView LibThreading)

if (HAVE_PULSEAUDIO)
target_compile_definitions(WebContent PRIVATE HAVE_PULSEAUDIO=1)
Expand Down
1 change: 1 addition & 0 deletions Meta/gn/secondary/Ladybird/WebContent/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ executable("WebContent") {
"//Userland/Services/WebContent/ConsoleGlobalEnvironmentExtensions.cpp",
"//Userland/Services/WebContent/PageClient.cpp",
"//Userland/Services/WebContent/PageHost.cpp",
"//Userland/Services/WebContent/RenderLoopExecutor.cpp",
"//Userland/Services/WebContent/WebContentConsoleClient.cpp",
"//Userland/Services/WebContent/WebDriverConnection.cpp",
"main.cpp",
Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibWeb/Page/Page.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class PageClient : public JS::Cell {
virtual DevicePixelRect screen_rect() const = 0;
virtual double device_pixels_per_css_pixel() const = 0;
virtual CSS::PreferredColorScheme preferred_color_scheme() const = 0;
virtual void paint(DevicePixelRect const&, Gfx::Bitmap&, PaintOptions = {}) = 0;
virtual NonnullOwnPtr<Web::Painting::RecordingPainter> paint(DevicePixelRect const&, PaintOptions = {}) = 0;
virtual void page_did_change_title(ByteString const&) { }
virtual void page_did_request_navigate_back() { }
virtual void page_did_request_navigate_forward() { }
Expand Down
2 changes: 2 additions & 0 deletions Userland/Libraries/LibWeb/Painting/RecordingPainter.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ class PaintingCommandExecutor {
};

class RecordingPainter {
AK_MAKE_NONCOPYABLE(RecordingPainter);

public:
void fill_rect(Gfx::IntRect const& rect, Color color);

Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class SVGDecodedImageData::SVGPageClient final : public PageClient {
virtual double device_pixels_per_css_pixel() const override { return 1.0; }
virtual CSS::PreferredColorScheme preferred_color_scheme() const override { return m_host_page.client().preferred_color_scheme(); }
virtual void request_file(FileRequest) override { }
virtual void paint(DevicePixelRect const&, Gfx::Bitmap&, Web::PaintOptions = {}) override { }
virtual NonnullOwnPtr<Web::Painting::RecordingPainter> paint(DevicePixelRect const&, Web::PaintOptions = {}) override { VERIFY_NOT_REACHED(); }

private:
explicit SVGPageClient(Page& host_page)
Expand Down
3 changes: 2 additions & 1 deletion Userland/Services/WebContent/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(SOURCES
ConnectionFromClient.cpp
ConsoleGlobalEnvironmentExtensions.cpp
ImageCodecPluginSerenity.cpp
RenderLoopExecutor.cpp
PageClient.cpp
PageHost.cpp
WebContentConsoleClient.cpp
Expand All @@ -31,7 +32,7 @@ set(GENERATED_SOURCES
)

serenity_bin(WebContent)
target_link_libraries(WebContent PRIVATE LibCore LibFileSystem LibIPC LibGfx LibAudio LibImageDecoderClient LibJS LibWebView LibWeb LibLocale LibMain)
target_link_libraries(WebContent PRIVATE LibCore LibFileSystem LibIPC LibGfx LibAudio LibImageDecoderClient LibJS LibWebView LibWeb LibLocale LibMain LibThreading)
link_with_locale_data(WebContent)

if (HAS_ACCELERATED_GRAPHICS)
Expand Down
9 changes: 7 additions & 2 deletions Userland/Services/WebContent/ConnectionFromClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <LibWeb/Loader/ProxyMappings.h>
#include <LibWeb/Loader/ResourceLoader.h>
#include <LibWeb/Namespace.h>
#include <LibWeb/Painting/PaintingCommandExecutorCPU.h>
#include <LibWeb/Painting/StackingContext.h>
#include <LibWeb/Painting/ViewportPaintable.h>
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
Expand Down Expand Up @@ -807,7 +808,9 @@ void ConnectionFromClient::take_document_screenshot(u64 page_id)
Web::DevicePixelRect rect { { 0, 0 }, content_size };

auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect.size().to_type<int>()).release_value_but_fixme_should_propagate_errors();
page(page_id).paint(rect, *bitmap);
auto recording_painter = page(page_id).paint(rect);
Web::Painting::PaintingCommandExecutorCPU painting_command_executor(*bitmap);
recording_painter->execute(painting_command_executor);

async_did_take_screenshot(page_id, bitmap->to_shareable_bitmap());
}
Expand All @@ -823,7 +826,9 @@ void ConnectionFromClient::take_dom_node_screenshot(u64 page_id, i32 node_id)
auto rect = page(page_id).page().enclosing_device_rect(dom_node->paintable_box()->absolute_border_box_rect());

auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect.size().to_type<int>()).release_value_but_fixme_should_propagate_errors();
page(page_id).paint(rect, *bitmap, { .paint_overlay = Web::PaintOptions::PaintOverlay::No });
auto recording_painter = page(page_id).paint(rect);
Web::Painting::PaintingCommandExecutorCPU painting_command_executor(*bitmap);
recording_painter->execute(painting_command_executor);

async_did_take_screenshot(page_id, bitmap->to_shareable_bitmap());
}
Expand Down
69 changes: 16 additions & 53 deletions Userland/Services/WebContent/PageClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/PaintingCommandExecutorCPU.h>
#include <LibWeb/Painting/ViewportPaintable.h>
#include <LibWeb/Platform/Timer.h>
#include <LibWebView/Attribute.h>
Expand All @@ -31,10 +30,6 @@
#include <WebContent/WebContentClientEndpoint.h>
#include <WebContent/WebDriverConnection.h>

#ifdef HAS_ACCELERATED_GRAPHICS
# include <LibWeb/Painting/PaintingCommandExecutorGPU.h>
#endif

namespace WebContent {

static bool s_use_gpu_painter = false;
Expand All @@ -53,36 +48,20 @@ PageClient::PageClient(PageHost& owner, u64 id)
: m_owner(owner)
, m_page(Web::Page::create(Web::Bindings::main_thread_vm(), *this))
, m_id(id)
, m_render_loop_executor(s_use_gpu_painter)
{
setup_palette();
m_render_loop_executor.start();

m_repaint_timer = Web::Platform::Timer::create_single_shot(0, [this] {
if (!m_backing_stores.back_bitmap) {
return;
}

auto& back_bitmap = *m_backing_stores.back_bitmap;
auto viewport_rect = page().css_to_device_rect(page().top_level_traversable()->viewport_rect());
paint(viewport_rect, back_bitmap);
auto recording_painter = paint(viewport_rect);

auto& backing_stores = m_backing_stores;
swap(backing_stores.front_bitmap, backing_stores.back_bitmap);
swap(backing_stores.front_bitmap_id, backing_stores.back_bitmap_id);

m_paint_state = PaintState::WaitingForClient;
client().async_did_paint(m_id, viewport_rect.to_type<int>(), backing_stores.front_bitmap_id);
m_render_loop_executor.paint(move(recording_painter), [this, viewport_rect](i32 front_bitmap_id) {
m_paint_state = PaintState::WaitingForClient;
client().async_did_paint(m_id, viewport_rect.to_type<int>(), front_bitmap_id);
});
});

#ifdef HAS_ACCELERATED_GRAPHICS
if (s_use_gpu_painter) {
auto context = AccelGfx::Context::create();
if (context.is_error()) {
dbgln("Failed to create AccelGfx context: {}", context.error());
VERIFY_NOT_REACHED();
}
m_accelerated_graphics_context = context.release_value();
}
#endif
}

void PageClient::schedule_repaint()
Expand All @@ -98,6 +77,8 @@ void PageClient::schedule_repaint()

void PageClient::ready_to_paint()
{
m_render_loop_executor.ready_to_paint();

auto old_paint_state = exchange(m_paint_state, PaintState::Ready);

if (old_paint_state == PaintState::PaintWhenReady)
Expand All @@ -106,10 +87,7 @@ void PageClient::ready_to_paint()

void PageClient::add_backing_store(i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap)
{
m_backing_stores.front_bitmap_id = front_bitmap_id;
m_backing_stores.back_bitmap_id = back_bitmap_id;
m_backing_stores.front_bitmap = *const_cast<Gfx::ShareableBitmap&>(front_bitmap).bitmap();
m_backing_stores.back_bitmap = *const_cast<Gfx::ShareableBitmap&>(back_bitmap).bitmap();
m_render_loop_executor.add_backing_store(front_bitmap_id, front_bitmap, back_bitmap_id, back_bitmap);
}

void PageClient::visit_edges(JS::Cell::Visitor& visitor)
Expand Down Expand Up @@ -187,35 +165,20 @@ Web::Layout::Viewport* PageClient::layout_root()
return document->layout_node();
}

void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& target, Web::PaintOptions paint_options)
NonnullOwnPtr<Web::Painting::RecordingPainter> PageClient::paint(Web::DevicePixelRect const& content_rect, Web::PaintOptions paint_options)
{
Web::Painting::RecordingPainter recording_painter;
NonnullOwnPtr<Web::Painting::RecordingPainter> recording_painter = make<Web::Painting::RecordingPainter>();

Gfx::IntRect bitmap_rect { {}, content_rect.size().to_type<int>() };
recording_painter.fill_rect(bitmap_rect, Web::CSS::SystemColor::canvas());
recording_painter->fill_rect(bitmap_rect, Web::CSS::SystemColor::canvas());

Web::HTML::Navigable::PaintConfig paint_config;
paint_config.paint_overlay = paint_options.paint_overlay == Web::PaintOptions::PaintOverlay::Yes;
paint_config.should_show_line_box_borders = m_should_show_line_box_borders;
paint_config.has_focus = m_has_focus;
page().top_level_traversable()->paint(recording_painter, paint_config);

if (s_use_gpu_painter) {
#ifdef HAS_ACCELERATED_GRAPHICS
Web::Painting::PaintingCommandExecutorGPU painting_command_executor(*m_accelerated_graphics_context, target);
recording_painter.execute(painting_command_executor);
#else
static bool has_warned_about_configuration = false;

if (!has_warned_about_configuration) {
warnln("\033[31;1mConfigured to use GPU painter, but current platform does not have accelerated graphics\033[0m");
has_warned_about_configuration = true;
}
#endif
} else {
Web::Painting::PaintingCommandExecutorCPU painting_command_executor(target);
recording_painter.execute(painting_command_executor);
}
page().top_level_traversable()->paint(*recording_painter, paint_config);

return recording_painter;
}

void PageClient::set_viewport_rect(Web::DevicePixelRect const& rect)
Expand Down
22 changes: 4 additions & 18 deletions Userland/Services/WebContent/PageClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
#include <LibGfx/Rect.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/PixelUnits.h>
#include <WebContent/Forward.h>

#ifdef HAS_ACCELERATED_GRAPHICS
# include <LibAccelGfx/Context.h>
#endif
#include <WebContent/RenderLoopExecutor.h>

namespace WebContent {

Expand All @@ -35,7 +31,7 @@ class PageClient final : public Web::PageClient {

ErrorOr<void> connect_to_webdriver(ByteString const& webdriver_ipc_path);

virtual void paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap&, Web::PaintOptions = {}) override;
virtual NonnullOwnPtr<Web::Painting::RecordingPainter> paint(Web::DevicePixelRect const& content_rect, Web::PaintOptions = {}) override;

void set_palette_impl(Gfx::PaletteImpl&);
void set_viewport_rect(Web::DevicePixelRect const&);
Expand Down Expand Up @@ -175,22 +171,12 @@ class PageClient final : public Web::PageClient {

RefPtr<WebDriverConnection> m_webdriver;

#ifdef HAS_ACCELERATED_GRAPHICS
OwnPtr<AccelGfx::Context> m_accelerated_graphics_context;
#endif

struct BackingStores {
i32 front_bitmap_id { -1 };
i32 back_bitmap_id { -1 };
RefPtr<Gfx::Bitmap> front_bitmap;
RefPtr<Gfx::Bitmap> back_bitmap;
};
BackingStores m_backing_stores;

HashMap<Web::DOM::Document*, NonnullOwnPtr<WebContentConsoleClient>> m_console_clients;
WeakPtr<WebContentConsoleClient> m_top_level_document_console_client;

JS::Handle<JS::GlobalObject> m_console_global_object;

RenderLoopExecutor m_render_loop_executor;
};

}
Loading

0 comments on commit 9f54dc9

Please sign in to comment.