From b1205f0aa1396f9aab87408d82657b9a1908780c Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 27 Apr 2024 20:15:28 +0000 Subject: [PATCH] LibWeb: Skip unnecessary sample corner and blit corner commands Before this change we were recording and executing sample/blit commands for each painting phase, even if there are no painting commands in-between sample and blit that produce result visible on a canvas. This change adds an optimization pass that goes through recorded painting commands list and marks sample and blit commands that could be skipped. Reduces sample and blit corners executing from 17% to 8% on Discord. --- Userland/Libraries/LibWeb/HTML/Navigable.cpp | 1 + .../Libraries/LibWeb/Painting/CommandList.cpp | 45 ++++++++++++++++++- .../Libraries/LibWeb/Painting/CommandList.h | 6 ++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index 83e9b7bff76..61a0bbffa06 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -2164,6 +2164,7 @@ void Navigable::paint(Painting::RecordingPainter& recording_painter, PaintConfig scroll_offsets_by_frame_id[scrollable_frame->id] = scroll_offset; } recording_painter.commands_list().apply_scroll_offsets(scroll_offsets_by_frame_id); + recording_painter.commands_list().mark_unnecessary_commands(); } m_needs_repaint = false; diff --git a/Userland/Libraries/LibWeb/Painting/CommandList.cpp b/Userland/Libraries/LibWeb/Painting/CommandList.cpp index 542cb92095f..3d0a79ee7c0 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandList.cpp +++ b/Userland/Libraries/LibWeb/Painting/CommandList.cpp @@ -39,6 +39,43 @@ void CommandList::apply_scroll_offsets(Vector const& offsets_by_f } } +void CommandList::mark_unnecessary_commands() +{ + // The pair sample_under_corners and blit_corner_clipping commands is not needed if there are no painting commands + // in between them that produce visible output. + struct SampleCornersBlitCornersRange { + u32 sample_command_index; + bool has_painting_commands_in_between { false }; + }; + // Stack of sample_under_corners commands that have not been matched with a blit_corner_clipping command yet. + Vector sample_blit_ranges; + for (u32 command_index = 0; command_index < m_commands.size(); ++command_index) { + auto const& command = m_commands[command_index].command; + if (command.has()) { + sample_blit_ranges.append({ + .sample_command_index = command_index, + .has_painting_commands_in_between = false, + }); + } else if (command.has()) { + auto range = sample_blit_ranges.take_last(); + if (!range.has_painting_commands_in_between) { + m_commands[range.sample_command_index].skip = true; + m_commands[command_index].skip = true; + } + } else { + // SetClipRect and ClearClipRect commands do not produce visible output + auto update_clip_command = command.has() || command.has(); + if (sample_blit_ranges.size() > 0 && !update_clip_command) { + // If painting command is found for sample_under_corners command on top of the stack, then all + // sample_under_corners commands below should also not be skipped. + for (auto& sample_blit_range : sample_blit_ranges) + sample_blit_range.has_painting_commands_in_between = true; + } + } + } + VERIFY(sample_blit_ranges.is_empty()); +} + void CommandList::execute(CommandExecutor& executor) { executor.prepare_to_execute(); @@ -76,8 +113,12 @@ void CommandList::execute(CommandExecutor& executor) HashTable skipped_sample_corner_commands; size_t next_command_index = 0; while (next_command_index < m_commands.size()) { - auto& command_with_scroll_id = m_commands[next_command_index++]; - auto& command = command_with_scroll_id.command; + if (m_commands[next_command_index].skip) { + next_command_index++; + continue; + } + + auto& command = m_commands[next_command_index++].command; auto bounding_rect = command_bounding_rectangle(command); if (bounding_rect.has_value() && (bounding_rect->is_empty() || executor.would_be_fully_clipped_by_painter(*bounding_rect))) { if (command.has()) { diff --git a/Userland/Libraries/LibWeb/Painting/CommandList.h b/Userland/Libraries/LibWeb/Painting/CommandList.h index e856235596c..80c5186a4dc 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandList.h +++ b/Userland/Libraries/LibWeb/Painting/CommandList.h @@ -96,15 +96,17 @@ public: void append(Command&& command, Optional scroll_frame_id); void apply_scroll_offsets(Vector const& offsets_by_frame_id); + void mark_unnecessary_commands(); void execute(CommandExecutor&); private: - struct CommandWithScrollFrame { + struct CommandListItem { Optional scroll_frame_id; Command command; + bool skip { false }; }; - AK::SegmentedVector m_commands; + AK::SegmentedVector m_commands; }; }