LibWeb: Add save and restore commands in recording painter

This change is a preparation before introducing Skia painter in an
upcoming change. It's needed because Skia does not have an API to
implement ClearClipRect command. It only allows to return previous
clip rect by popping from its internal state stack.

A bit more context: initially we had save and restore commands, but
their frequent use led to many reallocations of vector during painting
commands recording. To resolve this, we switched to SegmentedVector to
store commands list, which allows fast appends. Now, having many save
and restore commands no longer causes noticeable performance issue.
This commit is contained in:
Aliaksandr Kalenik 2024-06-10 14:18:32 +03:00 committed by Andreas Kling
commit 8a7cd8055f
Notes: sideshowbarker 2024-07-17 06:51:40 +09:00
10 changed files with 58 additions and 49 deletions

View file

@ -64,16 +64,22 @@ CommandResult AffineCommandExecutorCPU::draw_scaled_immutable_bitmap(DrawScaledI
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult AffineCommandExecutorCPU::set_clip_rect(SetClipRect const&) CommandResult AffineCommandExecutorCPU::save(Save const&)
{ {
// FIXME: Implement. The plan here is to implement https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm m_painter.save();
// within the rasterizer (which should work as the clip quadrilateral will always be convex).
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult AffineCommandExecutorCPU::clear_clip_rect(ClearClipRect const&) CommandResult AffineCommandExecutorCPU::restore(Restore const&)
{ {
// FIXME: Implement. m_painter.restore();
return CommandResult::Continue;
}
CommandResult AffineCommandExecutorCPU::add_clip_rect(AddClipRect const&)
{
// FIXME: Implement. The plan here is to implement https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
// within the rasterizer (which should work as the clip quadrilateral will always be convex).
return CommandResult::Continue; return CommandResult::Continue;
} }

View file

@ -19,8 +19,9 @@ public:
CommandResult fill_rect(FillRect const&) override; CommandResult fill_rect(FillRect const&) override;
CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override; CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override;
CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override; CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override;
CommandResult set_clip_rect(SetClipRect const&) override; CommandResult save(Save const&) override;
CommandResult clear_clip_rect(ClearClipRect const&) override; CommandResult restore(Restore const&) override;
CommandResult add_clip_rect(AddClipRect const&) override;
CommandResult push_stacking_context(PushStackingContext const&) override; CommandResult push_stacking_context(PushStackingContext const&) override;
CommandResult pop_stacking_context(PopStackingContext const&) override; CommandResult pop_stacking_context(PopStackingContext const&) override;
CommandResult paint_linear_gradient(PaintLinearGradient const&) override; CommandResult paint_linear_gradient(PaintLinearGradient const&) override;

View file

@ -92,12 +92,13 @@ struct DrawScaledImmutableBitmap {
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
}; };
struct SetClipRect { struct Save { };
struct Restore { };
struct AddClipRect {
Gfx::IntRect rect; Gfx::IntRect rect;
}; };
struct ClearClipRect { };
struct StackingContextTransform { struct StackingContextTransform {
Gfx::FloatPoint origin; Gfx::FloatPoint origin;
Gfx::FloatMatrix4x4 matrix; Gfx::FloatMatrix4x4 matrix;
@ -372,8 +373,9 @@ using Command = Variant<
FillRect, FillRect,
DrawScaledBitmap, DrawScaledBitmap,
DrawScaledImmutableBitmap, DrawScaledImmutableBitmap,
SetClipRect, Save,
ClearClipRect, Restore,
AddClipRect,
PushStackingContext, PushStackingContext,
PopStackingContext, PopStackingContext,
PaintLinearGradient, PaintLinearGradient,

View file

@ -107,17 +107,21 @@ CommandResult CommandExecutorCPU::draw_scaled_immutable_bitmap(DrawScaledImmutab
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult CommandExecutorCPU::set_clip_rect(SetClipRect const& command) CommandResult CommandExecutorCPU::save(Save const&)
{ {
auto& painter = this->painter(); painter().save();
painter.clear_clip_rect();
painter.add_clip_rect(command.rect);
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult CommandExecutorCPU::clear_clip_rect(ClearClipRect const&) CommandResult CommandExecutorCPU::restore(Restore const&)
{ {
painter().clear_clip_rect(); painter().restore();
return CommandResult::Continue;
}
CommandResult CommandExecutorCPU::add_clip_rect(AddClipRect const& command)
{
painter().add_clip_rect(command.rect);
return CommandResult::Continue; return CommandResult::Continue;
} }

View file

@ -20,8 +20,9 @@ public:
CommandResult fill_rect(FillRect const&) override; CommandResult fill_rect(FillRect const&) override;
CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override; CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override;
CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override; CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override;
CommandResult set_clip_rect(SetClipRect const&) override; CommandResult save(Save const&) override;
CommandResult clear_clip_rect(ClearClipRect const&) override; CommandResult restore(Restore const&) override;
CommandResult add_clip_rect(AddClipRect const&) override;
CommandResult push_stacking_context(PushStackingContext const&) override; CommandResult push_stacking_context(PushStackingContext const&) override;
CommandResult pop_stacking_context(PopStackingContext const&) override; CommandResult pop_stacking_context(PopStackingContext const&) override;
CommandResult paint_linear_gradient(PaintLinearGradient const&) override; CommandResult paint_linear_gradient(PaintLinearGradient const&) override;

View file

@ -89,15 +89,21 @@ CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(DrawScaledImmutab
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult CommandExecutorGPU::set_clip_rect(SetClipRect const& command) CommandResult CommandExecutorGPU::save(Save const&)
{ {
painter().set_clip_rect(command.rect); painter().save();
return CommandResult::Continue; return CommandResult::Continue;
} }
CommandResult CommandExecutorGPU::clear_clip_rect(ClearClipRect const&) CommandResult CommandExecutorGPU::restore(Restore const&)
{ {
painter().clear_clip_rect(); painter().restore();
return CommandResult::Continue;
}
CommandResult CommandExecutorGPU::add_clip_rect(AddClipRect const& command)
{
painter().set_clip_rect(command.rect);
return CommandResult::Continue; return CommandResult::Continue;
} }

View file

@ -19,8 +19,9 @@ public:
CommandResult fill_rect(FillRect const&) override; CommandResult fill_rect(FillRect const&) override;
CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override; CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override;
CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override; CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override;
CommandResult set_clip_rect(SetClipRect const&) override; CommandResult save(Save const&) override;
CommandResult clear_clip_rect(ClearClipRect const&) override; CommandResult restore(Restore const&) override;
CommandResult add_clip_rect(AddClipRect const&) override;
CommandResult push_stacking_context(PushStackingContext const&) override; CommandResult push_stacking_context(PushStackingContext const&) override;
CommandResult pop_stacking_context(PopStackingContext const&) override; CommandResult pop_stacking_context(PopStackingContext const&) override;
CommandResult paint_linear_gradient(PaintLinearGradient const&) override; CommandResult paint_linear_gradient(PaintLinearGradient const&) override;

View file

@ -63,8 +63,8 @@ void CommandList::mark_unnecessary_commands()
m_commands[command_index].skip = true; m_commands[command_index].skip = true;
} }
} else { } else {
// SetClipRect and ClearClipRect commands do not produce visible output // Save, Restore and AddClipRect commands do not produce visible output
auto update_clip_command = command.has<SetClipRect>() || command.has<ClearClipRect>(); auto update_clip_command = command.has<Save>() || command.has<Restore>() || command.has<AddClipRect>();
if (sample_blit_ranges.size() > 0 && !update_clip_command) { 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 // 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. // sample_under_corners commands below should also not be skipped.
@ -154,8 +154,9 @@ void CommandList::execute(CommandExecutor& executor)
else HANDLE_COMMAND(FillRect, fill_rect) else HANDLE_COMMAND(FillRect, fill_rect)
else HANDLE_COMMAND(DrawScaledBitmap, draw_scaled_bitmap) else HANDLE_COMMAND(DrawScaledBitmap, draw_scaled_bitmap)
else HANDLE_COMMAND(DrawScaledImmutableBitmap, draw_scaled_immutable_bitmap) else HANDLE_COMMAND(DrawScaledImmutableBitmap, draw_scaled_immutable_bitmap)
else HANDLE_COMMAND(SetClipRect, set_clip_rect) else HANDLE_COMMAND(AddClipRect, add_clip_rect)
else HANDLE_COMMAND(ClearClipRect, clear_clip_rect) else HANDLE_COMMAND(Save, save)
else HANDLE_COMMAND(Restore, restore)
else HANDLE_COMMAND(PushStackingContext, push_stacking_context) else HANDLE_COMMAND(PushStackingContext, push_stacking_context)
else HANDLE_COMMAND(PopStackingContext, pop_stacking_context) else HANDLE_COMMAND(PopStackingContext, pop_stacking_context)
else HANDLE_COMMAND(PaintLinearGradient, paint_linear_gradient) else HANDLE_COMMAND(PaintLinearGradient, paint_linear_gradient)

View file

@ -52,8 +52,9 @@ public:
virtual CommandResult fill_rect(FillRect const&) = 0; virtual CommandResult fill_rect(FillRect const&) = 0;
virtual CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) = 0; virtual CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) = 0;
virtual CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) = 0; virtual CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) = 0;
virtual CommandResult set_clip_rect(SetClipRect const&) = 0; virtual CommandResult save(Save const&) = 0;
virtual CommandResult clear_clip_rect(ClearClipRect const&) = 0; virtual CommandResult restore(Restore const&) = 0;
virtual CommandResult add_clip_rect(AddClipRect const&) = 0;
virtual CommandResult push_stacking_context(PushStackingContext const&) = 0; virtual CommandResult push_stacking_context(PushStackingContext const&) = 0;
virtual CommandResult pop_stacking_context(PopStackingContext const&) = 0; virtual CommandResult pop_stacking_context(PopStackingContext const&) = 0;
virtual CommandResult paint_linear_gradient(PaintLinearGradient const&) = 0; virtual CommandResult paint_linear_gradient(PaintLinearGradient const&) = 0;

View file

@ -253,15 +253,7 @@ void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun
void RecordingPainter::add_clip_rect(Gfx::IntRect const& rect) void RecordingPainter::add_clip_rect(Gfx::IntRect const& rect)
{ {
auto prev_clip_rect = state().clip_rect; append(AddClipRect { .rect = state().translation.map(rect) });
if (!state().clip_rect.has_value()) {
state().clip_rect = state().translation.map(rect);
} else {
state().clip_rect->intersect(state().translation.map(rect));
}
if (prev_clip_rect != state().clip_rect)
append(SetClipRect { .rect = *state().clip_rect });
} }
void RecordingPainter::translate(int dx, int dy) void RecordingPainter::translate(int dx, int dy)
@ -276,22 +268,16 @@ void RecordingPainter::translate(Gfx::IntPoint delta)
void RecordingPainter::save() void RecordingPainter::save()
{ {
append(Save {});
m_state_stack.append(m_state_stack.last()); m_state_stack.append(m_state_stack.last());
} }
void RecordingPainter::restore() void RecordingPainter::restore()
{ {
auto prev_clip_rect = state().clip_rect; append(Restore {});
VERIFY(m_state_stack.size() > 1); VERIFY(m_state_stack.size() > 1);
m_state_stack.take_last(); m_state_stack.take_last();
if (state().clip_rect != prev_clip_rect) {
if (state().clip_rect.has_value())
append(SetClipRect { .rect = *state().clip_rect });
else
append(ClearClipRect {});
}
} }
void RecordingPainter::push_stacking_context(PushStackingContextParams params) void RecordingPainter::push_stacking_context(PushStackingContextParams params)