diff --git a/Ladybird/HelperProcess.cpp b/Ladybird/HelperProcess.cpp index fc02d06898d..3c6da4acd20 100644 --- a/Ladybird/HelperProcess.cpp +++ b/Ladybird/HelperProcess.cpp @@ -116,6 +116,8 @@ ErrorOr> launch_web_content_process( arguments.append("--use-lagom-networking"sv); if (web_content_options.enable_gpu_painting == Ladybird::EnableGPUPainting::Yes) arguments.append("--use-gpu-painting"sv); + if (web_content_options.enable_experimental_cpu_transforms == Ladybird::EnableExperimentalCPUTransforms::Yes) + arguments.append("--experimental-cpu-transforms"sv); if (web_content_options.wait_for_debugger == Ladybird::WaitForDebugger::Yes) arguments.append("--wait-for-debugger"sv); if (web_content_options.log_all_js_exceptions == Ladybird::LogAllJSExceptions::Yes) diff --git a/Ladybird/Qt/main.cpp b/Ladybird/Qt/main.cpp index 4ebe86c0c42..d4683e0a512 100644 --- a/Ladybird/Qt/main.cpp +++ b/Ladybird/Qt/main.cpp @@ -100,6 +100,7 @@ ErrorOr serenity_main(Main::Arguments arguments) bool enable_qt_networking = false; bool expose_internals_object = false; bool use_gpu_painting = false; + bool use_experimental_cpu_transform_support = false; bool debug_web_content = false; bool log_all_js_exceptions = false; bool enable_idl_tracing = false; @@ -113,6 +114,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(disable_sql_database, "Disable SQL database", "disable-sql-database"); args_parser.add_option(enable_qt_networking, "Enable Qt as the backend networking service", "enable-qt-networking"); args_parser.add_option(use_gpu_painting, "Enable GPU painting", "enable-gpu-painting"); + args_parser.add_option(use_experimental_cpu_transform_support, "Enable experimental CPU transform support", "experimental-cpu-transforms"); args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content"); args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate"); args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions"); @@ -177,6 +179,7 @@ ErrorOr serenity_main(Main::Arguments arguments) .executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))), .enable_callgrind_profiling = enable_callgrind_profiling ? Ladybird::EnableCallgrindProfiling::Yes : Ladybird::EnableCallgrindProfiling::No, .enable_gpu_painting = use_gpu_painting ? Ladybird::EnableGPUPainting::Yes : Ladybird::EnableGPUPainting::No, + .enable_experimental_cpu_transforms = use_experimental_cpu_transform_support ? Ladybird::EnableExperimentalCPUTransforms::Yes : Ladybird::EnableExperimentalCPUTransforms::No, .use_lagom_networking = enable_qt_networking ? Ladybird::UseLagomNetworking::No : Ladybird::UseLagomNetworking::Yes, .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No, .log_all_js_exceptions = log_all_js_exceptions ? Ladybird::LogAllJSExceptions::Yes : Ladybird::LogAllJSExceptions::No, diff --git a/Ladybird/Types.h b/Ladybird/Types.h index 46a4e425cb4..3565b15d477 100644 --- a/Ladybird/Types.h +++ b/Ladybird/Types.h @@ -20,6 +20,11 @@ enum class EnableGPUPainting { Yes }; +enum class EnableExperimentalCPUTransforms { + No, + Yes +}; + enum class IsLayoutTestMode { No, Yes @@ -55,6 +60,7 @@ struct WebContentOptions { String executable_path; EnableCallgrindProfiling enable_callgrind_profiling { EnableCallgrindProfiling::No }; EnableGPUPainting enable_gpu_painting { EnableGPUPainting::No }; + EnableExperimentalCPUTransforms enable_experimental_cpu_transforms { EnableExperimentalCPUTransforms::No }; IsLayoutTestMode is_layout_test_mode { IsLayoutTestMode::No }; UseLagomNetworking use_lagom_networking { UseLagomNetworking::Yes }; WaitForDebugger wait_for_debugger { WaitForDebugger::No }; diff --git a/Ladybird/WebContent/main.cpp b/Ladybird/WebContent/main.cpp index 8e37c0f5527..3fb4937a07d 100644 --- a/Ladybird/WebContent/main.cpp +++ b/Ladybird/WebContent/main.cpp @@ -97,6 +97,7 @@ ErrorOr serenity_main(Main::Arguments arguments) bool expose_internals_object = false; bool use_lagom_networking = false; bool use_gpu_painting = false; + bool use_experimental_cpu_transform_support = false; bool wait_for_debugger = false; bool log_all_js_exceptions = false; bool enable_idl_tracing = false; @@ -109,6 +110,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object"); args_parser.add_option(use_lagom_networking, "Enable Lagom servers for networking", "use-lagom-networking"); args_parser.add_option(use_gpu_painting, "Enable GPU painting", "use-gpu-painting"); + args_parser.add_option(use_experimental_cpu_transform_support, "Enable experimental CPU transform support", "experimental-cpu-transforms"); args_parser.add_option(wait_for_debugger, "Wait for debugger", "wait-for-debugger"); args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name"); args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions"); @@ -130,6 +132,10 @@ ErrorOr serenity_main(Main::Arguments arguments) WebContent::PageClient::set_use_gpu_painter(); } + if (use_experimental_cpu_transform_support) { + WebContent::PageClient::set_use_experimental_cpu_transform_support(); + } + #if defined(AK_OS_MACOS) if (!mach_server_name.is_empty()) { Core::Platform::register_with_mach_server(mach_server_name); diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index eec1844af0d..3e6532ee084 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -524,6 +524,7 @@ set(SOURCES Page/EventHandler.cpp Page/InputEvent.cpp Page/Page.cpp + Painting/AffineCommandExecutorCPU.cpp Painting/AudioPaintable.cpp Painting/BackgroundPainting.cpp Painting/BorderRadiiData.cpp diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp index 9fe7e1d09a7..a1d1662692f 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp @@ -15,8 +15,9 @@ namespace Web::Painting { -CommandExecutorCPU::CommandExecutorCPU(Gfx::Bitmap& bitmap) +CommandExecutorCPU::CommandExecutorCPU(Gfx::Bitmap& bitmap, bool enable_affine_command_executor) : m_target_bitmap(bitmap) + , m_enable_affine_command_executor(enable_affine_command_executor) { stacking_contexts.append({ .painter = AK::make(bitmap), .opacity = 1.0f, @@ -127,6 +128,21 @@ CommandResult CommandExecutorCPU::clear_clip_rect(ClearClipRect const&) CommandResult CommandExecutorCPU::push_stacking_context(PushStackingContext const& command) { + // FIXME: This extracts the affine 2D part of the full transformation matrix. + // Use the whole matrix when we get better transformation support in LibGfx or use LibGL for drawing the bitmap + auto affine_transform = Gfx::extract_2d_affine_transform(command.transform.matrix); + + if (m_enable_affine_command_executor && command.opacity == 1.0f && !affine_transform.is_identity_or_translation()) { + auto offset = command.is_fixed_position ? Gfx::IntPoint {} : painter().translation(); + auto full_transform = Gfx::AffineTransform {} + .set_translation((command.post_transform_translation + offset).to_type()) + .translate(command.transform.origin) + .multiply(affine_transform) + .translate(-command.transform.origin); + m_affine_command_executor = AffineCommandExecutorCPU(m_target_bitmap, full_transform, painter().clip_rect()); + return CommandResult::ContinueWithNestedExecutor; + } + painter().save(); if (command.is_fixed_position) painter().translate(-painter().translation()); @@ -148,10 +164,6 @@ CommandResult CommandExecutorCPU::push_stacking_context(PushStackingContext cons return CommandResult::Continue; } - // FIXME: This extracts the affine 2D part of the full transformation matrix. - // Use the whole matrix when we get better transformation support in LibGfx or use LibGL for drawing the bitmap - auto affine_transform = Gfx::extract_2d_affine_transform(command.transform.matrix); - if (command.opacity == 1.0f && affine_transform.is_identity_or_translation()) { // OPTIMIZATION: This is a simple translation use previous stacking context's painter. painter().translate(affine_transform.translation().to_rounded() + command.post_transform_translation); diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h index aceba4441e8..21aa1310169 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include namespace Web::Painting { @@ -53,10 +54,17 @@ public: bool needs_update_immutable_bitmap_texture_cache() const override { return false; } void update_immutable_bitmap_texture_cache(HashMap&) override {}; - CommandExecutorCPU(Gfx::Bitmap& bitmap); + CommandExecutorCPU(Gfx::Bitmap& bitmap, bool enable_affine_command_executor = false); + + CommandExecutor& nested_executor() override + { + return *m_affine_command_executor; + } private: Gfx::Bitmap& m_target_bitmap; + bool m_enable_affine_command_executor { false }; + Vector> m_corner_clippers_stack; struct StackingContext { @@ -71,6 +79,7 @@ private: [[nodiscard]] Gfx::Painter& painter() { return *stacking_contexts.last().painter; } Vector stacking_contexts; + Optional m_affine_command_executor; }; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index bab3b44736a..98c5c33084c 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -309,7 +309,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const border_radius_data.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x); borders_rect.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x); - context.recording_painter().paint_borders(context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), outline_data->to_device_pixels(context)); + paint_all_borders(context.recording_painter(), context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), outline_data->to_device_pixels(context)); } } @@ -390,7 +390,7 @@ void PaintableBox::paint_border(PaintContext& context) const .bottom = box_model().border.bottom == 0 ? CSS::BorderData() : computed_values().border_bottom(), .left = box_model().border.left == 0 ? CSS::BorderData() : computed_values().border_left(), }; - context.recording_painter().paint_borders(context.rounded_device_rect(absolute_border_box_rect()), normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context)); + paint_all_borders(context.recording_painter(), context.rounded_device_rect(absolute_border_box_rect()), normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context)); } void PaintableBox::paint_backdrop_filter(PaintContext& context) const diff --git a/Userland/Services/WebContent/PageClient.cpp b/Userland/Services/WebContent/PageClient.cpp index 6d77686dd5c..741f23a3a20 100644 --- a/Userland/Services/WebContent/PageClient.cpp +++ b/Userland/Services/WebContent/PageClient.cpp @@ -37,6 +37,7 @@ namespace WebContent { static bool s_use_gpu_painter = false; +static bool s_use_experimental_cpu_transform_support = false; JS_DEFINE_ALLOCATOR(PageClient); @@ -45,6 +46,11 @@ void PageClient::set_use_gpu_painter() s_use_gpu_painter = true; } +void PageClient::set_use_experimental_cpu_transform_support() +{ + s_use_experimental_cpu_transform_support = true; +} + JS::NonnullGCPtr PageClient::create(JS::VM& vm, PageHost& page_host, u64 id) { return vm.heap().allocate_without_realm(page_host, id); @@ -222,7 +228,7 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta } #endif } else { - Web::Painting::CommandExecutorCPU painting_command_executor(target); + Web::Painting::CommandExecutorCPU painting_command_executor(target, s_use_experimental_cpu_transform_support); painting_commands.execute(painting_command_executor); } } diff --git a/Userland/Services/WebContent/PageClient.h b/Userland/Services/WebContent/PageClient.h index 4774f68076e..eb982b8bb52 100644 --- a/Userland/Services/WebContent/PageClient.h +++ b/Userland/Services/WebContent/PageClient.h @@ -30,6 +30,7 @@ public: static JS::NonnullGCPtr create(JS::VM& vm, PageHost& page_host, u64 id); static void set_use_gpu_painter(); + static void set_use_experimental_cpu_transform_support(); virtual void schedule_repaint() override; virtual bool is_ready_to_paint() const override;