LibWeb: Add optimized painting command for repeated background
With this change, instead of recording a display list item for each instance of a repeated background, a new DrawRepeatedImmutableBitmap type is used. This allows the painter to use optimized repeated image painting and, when the GPU backend is used, avoid re-uploading the image texture for each repetition. Some screenshot tests are affected, but there are no visible regressions. https://null.com/games/chainstaff works a lof faster with this change.
This commit is contained in:
parent
a4b289ebac
commit
c5afe70f77
Notes:
github-actions[bot]
2024-07-24 09:13:52 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/c5afe70f779 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/786
13 changed files with 74 additions and 0 deletions
Binary file not shown.
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 364 KiB |
Binary file not shown.
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 272 KiB |
Binary file not shown.
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 114 KiB |
|
@ -141,6 +141,11 @@ void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_r
|
|||
}
|
||||
}
|
||||
|
||||
Gfx::ImmutableBitmap const* ImageStyleValue::current_frame_bitmap(DevicePixelRect const& dest_rect) const
|
||||
{
|
||||
return bitmap(m_current_frame_index, dest_rect.size().to_type<int>());
|
||||
}
|
||||
|
||||
JS::GCPtr<HTML::DecodedImageData> ImageStyleValue::image_data() const
|
||||
{
|
||||
if (!m_image_request)
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths = {}) const override;
|
||||
|
||||
virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;
|
||||
Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const;
|
||||
|
||||
Function<void()> on_animate;
|
||||
|
||||
|
|
|
@ -351,6 +351,8 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
|
|||
// Repetition
|
||||
bool repeat_x = false;
|
||||
bool repeat_y = false;
|
||||
bool repeat_x_has_gap = false;
|
||||
bool repeat_y_has_gap = false;
|
||||
CSSPixels x_step = 0;
|
||||
CSSPixels y_step = 0;
|
||||
|
||||
|
@ -368,6 +370,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
|
|||
auto space = fmod(background_positioning_area.width().to_double(), image_rect.width().to_double());
|
||||
x_step = image_rect.width() + CSSPixels::nearest_value_for(space / static_cast<double>(whole_images - 1));
|
||||
repeat_x = true;
|
||||
repeat_x_has_gap = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -399,6 +402,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
|
|||
auto space = fmod(background_positioning_area.height().to_float(), image_rect.height().to_float());
|
||||
y_step = image_rect.height() + CSSPixels::nearest_value_for(static_cast<double>(space) / static_cast<double>(whole_images - 1));
|
||||
repeat_y = true;
|
||||
repeat_y_has_gap = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -456,6 +460,13 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
|
|||
}
|
||||
});
|
||||
display_list_recorder.fill_rect(fill_rect->to_type<int>(), color.value(), clip_paths);
|
||||
} else if (is<CSS::ImageStyleValue>(image) && repeat_x && repeat_y && !repeat_x_has_gap && !repeat_y_has_gap) {
|
||||
// Use a dedicated painting command for repeated images instead of recording a separate command for each instance
|
||||
// of a repeated background, so the painter has the opportunity to optimize the painting of repeated images.
|
||||
auto dest_rect = context.rounded_device_rect(image_rect);
|
||||
auto const* bitmap = static_cast<CSS::ImageStyleValue const&>(image).current_frame_bitmap(dest_rect);
|
||||
auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap->rect(), dest_rect.to_type<int>());
|
||||
context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type<int>(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }, clip_paths);
|
||||
} else {
|
||||
for_each_image_device_rect([&](auto const& image_device_rect) {
|
||||
image.paint(context, image_device_rect, image_rendering, clip_paths);
|
||||
|
|
|
@ -75,6 +75,21 @@ struct DrawScaledImmutableBitmap {
|
|||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawRepeatedImmutableBitmap {
|
||||
struct Repeat {
|
||||
bool x { false };
|
||||
bool y { false };
|
||||
};
|
||||
|
||||
Gfx::IntRect dst_rect;
|
||||
NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
|
||||
Gfx::ScalingMode scaling_mode;
|
||||
Repeat repeat;
|
||||
Vector<Gfx::Path> clip_paths;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct Save { };
|
||||
struct Restore { };
|
||||
|
||||
|
@ -352,6 +367,7 @@ using Command = Variant<
|
|||
FillRect,
|
||||
DrawScaledBitmap,
|
||||
DrawScaledImmutableBitmap,
|
||||
DrawRepeatedImmutableBitmap,
|
||||
Save,
|
||||
Restore,
|
||||
AddClipRect,
|
||||
|
|
|
@ -152,6 +152,7 @@ void DisplayList::execute(DisplayListPlayer& executor)
|
|||
else HANDLE_COMMAND(FillRect, fill_rect)
|
||||
else HANDLE_COMMAND(DrawScaledBitmap, draw_scaled_bitmap)
|
||||
else HANDLE_COMMAND(DrawScaledImmutableBitmap, draw_scaled_immutable_bitmap)
|
||||
else HANDLE_COMMAND(DrawRepeatedImmutableBitmap, draw_repeated_immutable_bitmap)
|
||||
else HANDLE_COMMAND(AddClipRect, add_clip_rect)
|
||||
else HANDLE_COMMAND(Save, save)
|
||||
else HANDLE_COMMAND(Restore, restore)
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
virtual CommandResult fill_rect(FillRect const&) = 0;
|
||||
virtual CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) = 0;
|
||||
virtual CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) = 0;
|
||||
virtual CommandResult draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) = 0;
|
||||
virtual CommandResult save(Save const&) = 0;
|
||||
virtual CommandResult restore(Restore const&) = 0;
|
||||
virtual CommandResult add_clip_rect(AddClipRect const&) = 0;
|
||||
|
|
|
@ -459,6 +459,31 @@ CommandResult DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmu
|
|||
return CommandResult::Continue;
|
||||
}
|
||||
|
||||
CommandResult DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command)
|
||||
{
|
||||
APPLY_PATH_CLIP_IF_NEEDED
|
||||
|
||||
auto bitmap = to_skia_bitmap(command.bitmap->bitmap());
|
||||
auto image = SkImages::RasterFromBitmap(bitmap);
|
||||
|
||||
SkMatrix matrix;
|
||||
auto dst_rect = command.dst_rect.to_type<float>();
|
||||
auto src_size = command.bitmap->size().to_type<float>();
|
||||
matrix.setScale(dst_rect.width() / src_size.width(), dst_rect.height() / src_size.height());
|
||||
matrix.postTranslate(dst_rect.x(), dst_rect.y());
|
||||
auto sampling_options = to_skia_sampling_options(command.scaling_mode);
|
||||
|
||||
auto tile_mode_x = command.repeat.x ? SkTileMode::kRepeat : SkTileMode::kDecal;
|
||||
auto tile_mode_y = command.repeat.y ? SkTileMode::kRepeat : SkTileMode::kDecal;
|
||||
auto shader = image->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setShader(shader);
|
||||
auto& canvas = surface().canvas();
|
||||
canvas.drawPaint(paint);
|
||||
return CommandResult::Continue;
|
||||
}
|
||||
|
||||
CommandResult DisplayListPlayerSkia::add_clip_rect(AddClipRect const& command)
|
||||
{
|
||||
auto& canvas = surface().canvas();
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
CommandResult fill_rect(FillRect const&) override;
|
||||
CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override;
|
||||
CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override;
|
||||
CommandResult draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) override;
|
||||
CommandResult add_clip_rect(AddClipRect const&) override;
|
||||
CommandResult save(Save const&) override;
|
||||
CommandResult restore(Restore const&) override;
|
||||
|
|
|
@ -210,6 +210,17 @@ void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_r
|
|||
});
|
||||
}
|
||||
|
||||
void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat, Vector<Gfx::Path> const& clip_paths)
|
||||
{
|
||||
append(DrawRepeatedImmutableBitmap {
|
||||
.dst_rect = dst_rect,
|
||||
.bitmap = move(bitmap),
|
||||
.scaling_mode = scaling_mode,
|
||||
.repeat = repeat,
|
||||
.clip_paths = clip_paths,
|
||||
});
|
||||
}
|
||||
|
||||
void DisplayListRecorder::draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness, Gfx::LineStyle style, Color alternate_color)
|
||||
{
|
||||
append(DrawLine {
|
||||
|
|
|
@ -89,6 +89,8 @@ public:
|
|||
void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
|
||||
void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor, Vector<Gfx::Path> const& clip_paths = {});
|
||||
|
||||
void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat, Vector<Gfx::Path> const& clip_paths = {});
|
||||
|
||||
void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent);
|
||||
|
||||
void draw_text(Gfx::IntRect const&, String, Gfx::Font const&, Gfx::TextAlignment, Color);
|
||||
|
|
Loading…
Add table
Reference in a new issue