Ver código fonte

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.
Aliaksandr Kalenik 11 meses atrás
pai
commit
c5afe70f77

BIN
Tests/LibWeb/Screenshot/images/border-radius-ref.png


BIN
Tests/LibWeb/Screenshot/images/css-background-clip-text.png


BIN
Tests/LibWeb/Screenshot/images/css-backgrounds-ref.png


+ 5 - 0
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp

@@ -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)

+ 1 - 0
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h

@@ -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;
 

+ 11 - 0
Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp

@@ -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);

+ 16 - 0
Userland/Libraries/LibWeb/Painting/Command.h

@@ -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,

+ 1 - 0
Userland/Libraries/LibWeb/Painting/DisplayList.cpp

@@ -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)

+ 1 - 0
Userland/Libraries/LibWeb/Painting/DisplayList.h

@@ -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;

+ 25 - 0
Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp

@@ -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();

+ 1 - 0
Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h

@@ -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;

+ 11 - 0
Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp

@@ -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 {

+ 2 - 0
Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h

@@ -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);