Commit bf22e4c9 authored by Daniel Libby's avatar Daniel Libby Committed by Commit Bot

Consider directly composited images as covering the PictureTilingSet

For elements with will-change:transform or directly composited images
the raster scale is not updated in response to transform scale
changes. Once the scale gets especially large, the interest rect
computed by blink starts to become less than the layer bounds.
This ends up setting a recorded_viewport_ on RasterSource that is also
less than the layer bounds. If the interest rect gets small enough, it's
possible that tiles that are in view are not completely covered by the
RasterSource. This means that we won't create tiles (much less assign
them GPU memory and schedule them) in PictureLayerTiling::CreateTile. We
did not see this with the previous implementation of directly composited
images, since the content layer never had an interest rect computed.

This change fixes this specifically for directly composited images by
always considering its raster source as covering the entire layer. This
is the definition of directly composited images (has a DrawImageRectOp
that draws into the entire layer). This allows the in view tiles to be
created, then rasterized and activated and the image no longer
disappears.

I looked into fixing the general case (see comments in the bug) but
given the regression for the images scenario and the fact that we'd like
to get a fix merged for 85, I took an approach to just fix directly
composited images.

Bug: 1110141
Change-Id: Ice3cf25508585f116edd7317718292ec9ad8a7ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2358888Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Reviewed-by: default avatarvmpstr <vmpstr@chromium.org>
Commit-Queue: Daniel Libby <dlibby@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#798836}
parent 8a1dd3be
...@@ -120,6 +120,10 @@ class FixedInvalidationPictureLayerTilingClient ...@@ -120,6 +120,10 @@ class FixedInvalidationPictureLayerTilingClient
return base_client_->GetPaintWorkletRecords(); return base_client_->GetPaintWorkletRecords();
} }
bool IsDirectlyCompositedImage() const override {
return base_client_->IsDirectlyCompositedImage();
}
private: private:
PictureLayerTilingClient* base_client_; PictureLayerTilingClient* base_client_;
Region invalidation_; Region invalidation_;
......
...@@ -975,6 +975,10 @@ const PaintWorkletRecordMap& PictureLayerImpl::GetPaintWorkletRecords() const { ...@@ -975,6 +975,10 @@ const PaintWorkletRecordMap& PictureLayerImpl::GetPaintWorkletRecords() const {
return paint_worklet_records_; return paint_worklet_records_;
} }
bool PictureLayerImpl::IsDirectlyCompositedImage() const {
return directly_composited_image_size_.has_value();
}
gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const { gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const {
return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale()); return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale());
} }
......
...@@ -77,6 +77,7 @@ class CC_EXPORT PictureLayerImpl ...@@ -77,6 +77,7 @@ class CC_EXPORT PictureLayerImpl
bool HasValidTilePriorities() const override; bool HasValidTilePriorities() const override;
bool RequiresHighResToDraw() const override; bool RequiresHighResToDraw() const override;
const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; const PaintWorkletRecordMap& GetPaintWorkletRecords() const override;
bool IsDirectlyCompositedImage() const override;
// ImageAnimationController::AnimationDriver overrides. // ImageAnimationController::AnimationDriver overrides.
bool ShouldAnimate(PaintImage::Id paint_image_id) const override; bool ShouldAnimate(PaintImage::Id paint_image_id) const override;
......
...@@ -4885,6 +4885,51 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) { ...@@ -4885,6 +4885,51 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) {
EXPECT_EQ(tile22, active_tiling->TileAt(2, 2)->id()); EXPECT_EQ(tile22, active_tiling->TileAt(2, 2)->id());
} }
TEST_F(LegacySWPictureLayerImplTest,
DirectlyCompositedImageRasterSourceCoverage) {
gfx::Size tile_size(100, 100);
gfx::Size layer_bounds(400, 400);
scoped_refptr<FakeRasterSource> filled_raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
scoped_refptr<FakeRasterSource> partial_raster_source =
FakeRasterSource::CreatePartiallyFilled(layer_bounds,
gfx::Rect(150, 150, 100, 100));
SetupPendingTreeWithFixedTileSize(filled_raster_source, tile_size, Region());
pending_layer()->SetDirectlyCompositedImageSize(layer_bounds);
ActivateTree();
PictureLayerTiling* pending_tiling = old_pending_layer()->HighResTiling();
PictureLayerTiling* active_tiling = active_layer()->HighResTiling();
// We should have all tiles on active, and none on pending.
EXPECT_EQ(0u, pending_tiling->AllTilesForTesting().size());
EXPECT_EQ(5u * 5u, active_tiling->AllTilesForTesting().size());
// Now put a partially-recorded raster source on the pending tree (and
// invalidate everything, since the main thread recording will invalidate
// dropped recordings). Because the layer is a directly composited image, all
// tiles should be created.
SetupPendingTreeWithFixedTileSize(partial_raster_source, tile_size,
Region(gfx::Rect(layer_bounds)));
EXPECT_EQ(5u * 5u, pending_tiling->AllTilesForTesting().size());
// Activate the tree. The same tiles should have been moved to active tree.
EXPECT_EQ(5u * 5u, pending_tiling->AllTilesForTesting().size());
Tile::Id tile00 = pending_tiling->TileAt(0, 0)->id();
Tile::Id tile11 = pending_tiling->TileAt(1, 1)->id();
Tile::Id tile22 = pending_tiling->TileAt(2, 2)->id();
// Activate the tree. The tiles are moved to the active tree.
ActivateTree();
EXPECT_EQ(5u * 5u, active_tiling->AllTilesForTesting().size());
EXPECT_EQ(tile00, active_tiling->TileAt(0, 0)->id());
EXPECT_EQ(tile11, active_tiling->TileAt(1, 1)->id());
EXPECT_EQ(tile22, active_tiling->TileAt(2, 2)->id());
}
TEST_F(LegacySWPictureLayerImplTest, ScrollPastLiveTilesRectAndBack) { TEST_F(LegacySWPictureLayerImplTest, ScrollPastLiveTilesRectAndBack) {
host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1)); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "cc/paint/display_item_list.h" #include "cc/paint/display_item_list.h"
#include "cc/paint/image_provider.h" #include "cc/paint/image_provider.h"
#include "cc/paint/skia_paint_canvas.h" #include "cc/paint/skia_paint_canvas.h"
#include "cc/tiles/picture_layer_tiling.h"
#include "components/viz/common/traced_value.h" #include "components/viz/common/traced_value.h"
#include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/axis_transform2d.h" #include "ui/gfx/geometry/axis_transform2d.h"
...@@ -155,9 +156,18 @@ RasterSource::TakeDecodingModeMap() { ...@@ -155,9 +156,18 @@ RasterSource::TakeDecodingModeMap() {
return display_list_->TakeDecodingModeMap(); return display_list_->TakeDecodingModeMap();
} }
bool RasterSource::CoversRect(const gfx::Rect& layer_rect) const { bool RasterSource::CoversRect(const gfx::Rect& layer_rect,
const PictureLayerTilingClient& client) const {
if (size_.IsEmpty()) if (size_.IsEmpty())
return false; return false;
// Directly composited images by definition have a single DrawImageRectOp that
// covers the entire layer, so return true for these raster sources.
// TODO(crbug.com/1117174): This will miss cases when the raster source
// partially covers the layer rect.
if (client.IsDirectlyCompositedImage())
return true;
gfx::Rect bounded_rect = layer_rect; gfx::Rect bounded_rect = layer_rect;
bounded_rect.Intersect(gfx::Rect(size_)); bounded_rect.Intersect(gfx::Rect(size_));
return recorded_viewport_.Contains(bounded_rect); return recorded_viewport_.Contains(bounded_rect);
......
...@@ -26,6 +26,7 @@ namespace cc { ...@@ -26,6 +26,7 @@ namespace cc {
class DisplayItemList; class DisplayItemList;
class DrawImage; class DrawImage;
class ImageProvider; class ImageProvider;
class PictureLayerTilingClient;
class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
public: public:
...@@ -88,7 +89,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { ...@@ -88,7 +89,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
// Return true iff this raster source can raster the given rect in layer // Return true iff this raster source can raster the given rect in layer
// space. // space.
bool CoversRect(const gfx::Rect& layer_rect) const; bool CoversRect(const gfx::Rect& layer_rect,
const PictureLayerTilingClient& client) const;
// Returns true if this raster source has anything to rasterize. // Returns true if this raster source has anything to rasterize.
bool HasRecordings() const; bool HasRecordings() const;
......
...@@ -84,4 +84,8 @@ FakePictureLayerTilingClient::GetPaintWorkletRecords() const { ...@@ -84,4 +84,8 @@ FakePictureLayerTilingClient::GetPaintWorkletRecords() const {
return paint_worklet_records_; return paint_worklet_records_;
} }
bool FakePictureLayerTilingClient::IsDirectlyCompositedImage() const {
return false;
}
} // namespace cc } // namespace cc
...@@ -40,6 +40,7 @@ class FakePictureLayerTilingClient : public PictureLayerTilingClient { ...@@ -40,6 +40,7 @@ class FakePictureLayerTilingClient : public PictureLayerTilingClient {
const PictureLayerTiling* tiling) const override; const PictureLayerTiling* tiling) const override;
bool RequiresHighResToDraw() const override; bool RequiresHighResToDraw() const override;
const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; const PaintWorkletRecordMap& GetPaintWorkletRecords() const override;
bool IsDirectlyCompositedImage() const override;
void set_twin_tiling_set(PictureLayerTilingSet* set) { void set_twin_tiling_set(PictureLayerTilingSet* set) {
twin_set_ = set; twin_set_ = set;
......
...@@ -74,7 +74,7 @@ Tile* PictureLayerTiling::CreateTile(const Tile::CreateInfo& info) { ...@@ -74,7 +74,7 @@ Tile* PictureLayerTiling::CreateTile(const Tile::CreateInfo& info) {
TileMapKey key(i, j); TileMapKey key(i, j);
DCHECK(tiles_.find(key) == tiles_.end()); DCHECK(tiles_.find(key) == tiles_.end());
if (!raster_source_->CoversRect(info.enclosing_layer_rect)) if (!raster_source_->CoversRect(info.enclosing_layer_rect, *client_))
return nullptr; return nullptr;
all_tiles_done_ = false; all_tiles_done_ = false;
...@@ -338,7 +338,8 @@ bool PictureLayerTiling::ShouldCreateTileAt( ...@@ -338,7 +338,8 @@ bool PictureLayerTiling::ShouldCreateTileAt(
// If the active tree can't create a tile, because of its raster source, then // If the active tree can't create a tile, because of its raster source, then
// the pending tree should create one. // the pending tree should create one.
if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect)) if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect,
*active_twin->client()))
return true; return true;
const Region* layer_invalidation = client_->GetPendingInvalidation(); const Region* layer_invalidation = client_->GetPendingInvalidation();
...@@ -861,7 +862,7 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile( ...@@ -861,7 +862,7 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile(
Tile* tile, Tile* tile,
PriorityRectType priority_rect_type) const { PriorityRectType priority_rect_type) const {
DCHECK(tile); DCHECK(tile);
DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect())) DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect(), *client_))
<< "Recording rect: " << "Recording rect: "
<< EnclosingLayerRectFromContentsRect(tile->content_rect()).ToString(); << EnclosingLayerRectFromContentsRect(tile->content_rect()).ToString();
......
...@@ -50,6 +50,7 @@ class CC_EXPORT PictureLayerTilingClient { ...@@ -50,6 +50,7 @@ class CC_EXPORT PictureLayerTilingClient {
virtual bool HasValidTilePriorities() const = 0; virtual bool HasValidTilePriorities() const = 0;
virtual bool RequiresHighResToDraw() const = 0; virtual bool RequiresHighResToDraw() const = 0;
virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0; virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0;
virtual bool IsDirectlyCompositedImage() const = 0;
protected: protected:
virtual ~PictureLayerTilingClient() {} virtual ~PictureLayerTilingClient() {}
......
<!DOCTYPE html>
<html>
<style>
html { overflow: hidden; }
#change {
will-change:transform;
width:200vw;
height:200vh;
position:absolute;
top: 0px;
left: 0px;
}
</style>
<img id="change" src="image.png"></img>
<div id="placeholder" style="position:relative">div</div>
<!DOCTYPE html>
<html class="reftest-wait">
<title>Composited images correctly display under large scale transform changes</title>
<meta charset="utf-8">
<link rel="match" href="image-compositing-large-scale-change-ref.html"/>
<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
<style>
html { overflow: hidden; }
#change {
will-change:transform;
width:1000px;
height:1000px;
position:absolute;
top: calc(50% - 5px);
left: calc(50% - 5px);
}
</style>
<img id="change" src="image.png"></img>
<div id="placeholder" style="position:relative"></div>
<script>
window.onload = () => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
let image = document.querySelector('#change');
image.style.transform = 'scale(20)';
placeholder.innerText = "div";
requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
});
}
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment