mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-05 15:49:11 +00:00
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: c5afe70f77
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
|
JS::GCPtr<HTML::DecodedImageData> ImageStyleValue::image_data() const
|
||||||
{
|
{
|
||||||
if (!m_image_request)
|
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;
|
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;
|
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;
|
Function<void()> on_animate;
|
||||||
|
|
||||||
|
|
|
@ -351,6 +351,8 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
|
||||||
// Repetition
|
// Repetition
|
||||||
bool repeat_x = false;
|
bool repeat_x = false;
|
||||||
bool repeat_y = false;
|
bool repeat_y = false;
|
||||||
|
bool repeat_x_has_gap = false;
|
||||||
|
bool repeat_y_has_gap = false;
|
||||||
CSSPixels x_step = 0;
|
CSSPixels x_step = 0;
|
||||||
CSSPixels y_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());
|
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));
|
x_step = image_rect.width() + CSSPixels::nearest_value_for(space / static_cast<double>(whole_images - 1));
|
||||||
repeat_x = true;
|
repeat_x = true;
|
||||||
|
repeat_x_has_gap = true;
|
||||||
}
|
}
|
||||||
break;
|
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());
|
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));
|
y_step = image_rect.height() + CSSPixels::nearest_value_for(static_cast<double>(space) / static_cast<double>(whole_images - 1));
|
||||||
repeat_y = true;
|
repeat_y = true;
|
||||||
|
repeat_y_has_gap = true;
|
||||||
}
|
}
|
||||||
break;
|
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);
|
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 {
|
} else {
|
||||||
for_each_image_device_rect([&](auto const& image_device_rect) {
|
for_each_image_device_rect([&](auto const& image_device_rect) {
|
||||||
image.paint(context, image_device_rect, image_rendering, clip_paths);
|
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); }
|
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 Save { };
|
||||||
struct Restore { };
|
struct Restore { };
|
||||||
|
|
||||||
|
@ -352,6 +367,7 @@ using Command = Variant<
|
||||||
FillRect,
|
FillRect,
|
||||||
DrawScaledBitmap,
|
DrawScaledBitmap,
|
||||||
DrawScaledImmutableBitmap,
|
DrawScaledImmutableBitmap,
|
||||||
|
DrawRepeatedImmutableBitmap,
|
||||||
Save,
|
Save,
|
||||||
Restore,
|
Restore,
|
||||||
AddClipRect,
|
AddClipRect,
|
||||||
|
|
|
@ -152,6 +152,7 @@ void DisplayList::execute(DisplayListPlayer& 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(DrawRepeatedImmutableBitmap, draw_repeated_immutable_bitmap)
|
||||||
else HANDLE_COMMAND(AddClipRect, add_clip_rect)
|
else HANDLE_COMMAND(AddClipRect, add_clip_rect)
|
||||||
else HANDLE_COMMAND(Save, save)
|
else HANDLE_COMMAND(Save, save)
|
||||||
else HANDLE_COMMAND(Restore, restore)
|
else HANDLE_COMMAND(Restore, restore)
|
||||||
|
|
|
@ -45,6 +45,7 @@ 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 draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) = 0;
|
||||||
virtual CommandResult save(Save const&) = 0;
|
virtual CommandResult save(Save const&) = 0;
|
||||||
virtual CommandResult restore(Restore const&) = 0;
|
virtual CommandResult restore(Restore const&) = 0;
|
||||||
virtual CommandResult add_clip_rect(AddClipRect const&) = 0;
|
virtual CommandResult add_clip_rect(AddClipRect const&) = 0;
|
||||||
|
|
|
@ -459,6 +459,31 @@ CommandResult DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmu
|
||||||
return CommandResult::Continue;
|
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)
|
CommandResult DisplayListPlayerSkia::add_clip_rect(AddClipRect const& command)
|
||||||
{
|
{
|
||||||
auto& canvas = surface().canvas();
|
auto& canvas = surface().canvas();
|
||||||
|
|
|
@ -37,6 +37,7 @@ 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 draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) override;
|
||||||
CommandResult add_clip_rect(AddClipRect const&) override;
|
CommandResult add_clip_rect(AddClipRect const&) override;
|
||||||
CommandResult save(Save const&) override;
|
CommandResult save(Save const&) override;
|
||||||
CommandResult restore(Restore 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)
|
void DisplayListRecorder::draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness, Gfx::LineStyle style, Color alternate_color)
|
||||||
{
|
{
|
||||||
append(DrawLine {
|
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_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_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_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);
|
void draw_text(Gfx::IntRect const&, String, Gfx::Font const&, Gfx::TextAlignment, Color);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue