mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
When picture is updated and bitmapcanvas reused, clear element cache (flutter/engine#22072)
This commit is contained in:
parent
484d7d99f3
commit
42fc39e8a9
@ -135,7 +135,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
}
|
||||
|
||||
/// Setup cache for reusing DOM elements across frames.
|
||||
void setElementCache(CrossFrameCache<html.HtmlElement> cache) {
|
||||
void setElementCache(CrossFrameCache<html.HtmlElement>? cache) {
|
||||
_elementCache = cache;
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ List<_PaintRequest> _paintQueue = <_PaintRequest>[];
|
||||
|
||||
void _recycleCanvas(EngineCanvas? canvas) {
|
||||
if (canvas is BitmapCanvas) {
|
||||
canvas.setElementCache(null);
|
||||
if (canvas.isReusable()) {
|
||||
_recycledCanvases.add(canvas);
|
||||
if (_recycledCanvases.length > _kCanvasCacheSize) {
|
||||
@ -91,7 +92,7 @@ class PersistedPicture extends PersistedLeafSurface {
|
||||
final int hints;
|
||||
|
||||
/// Cache for reusing elements such as images across picture updates.
|
||||
CrossFrameCache<html.HtmlElement> _elementCache =
|
||||
CrossFrameCache<html.HtmlElement>? _elementCache =
|
||||
CrossFrameCache<html.HtmlElement>();
|
||||
|
||||
@override
|
||||
@ -386,6 +387,7 @@ class PersistedPicture extends PersistedLeafSurface {
|
||||
if (_debugShowCanvasReuseStats) {
|
||||
DebugCanvasReuseOverlay.instance.keptCount++;
|
||||
}
|
||||
// Re-use old bitmap canvas.
|
||||
oldCanvas.bounds = _optimalLocalCullRect!;
|
||||
_canvas = oldCanvas;
|
||||
oldCanvas.setElementCache(_elementCache);
|
||||
@ -395,6 +397,10 @@ class PersistedPicture extends PersistedLeafSurface {
|
||||
// We can't use the old canvas because the size has changed, so we put
|
||||
// it in a cache for later reuse.
|
||||
_recycleCanvas(oldCanvas);
|
||||
if (_canvas is BitmapCanvas) {
|
||||
(_canvas as BitmapCanvas).setElementCache(null);
|
||||
}
|
||||
_canvas = null;
|
||||
// We cannot paint immediately because not all canvases that we may be
|
||||
// able to reuse have been released yet. So instead we enqueue this
|
||||
// picture to be painted after the update cycle is done syncing the layer
|
||||
@ -403,8 +409,9 @@ class PersistedPicture extends PersistedLeafSurface {
|
||||
canvasSize: _optimalLocalCullRect!.size,
|
||||
paintCallback: () {
|
||||
_canvas = _findOrCreateCanvas(_optimalLocalCullRect!);
|
||||
assert(_canvas is BitmapCanvas &&
|
||||
(_canvas as BitmapCanvas?)!._elementCache == _elementCache);
|
||||
if (_canvas is BitmapCanvas) {
|
||||
(_canvas as BitmapCanvas).setElementCache(_elementCache);
|
||||
}
|
||||
if (_debugExplainSurfaceStats) {
|
||||
final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas;
|
||||
_surfaceStatsFor(this).paintPixelCount +=
|
||||
@ -518,6 +525,9 @@ class PersistedPicture extends PersistedLeafSurface {
|
||||
super.update(oldSurface);
|
||||
// Transfer element cache over.
|
||||
_elementCache = oldSurface._elementCache;
|
||||
if (oldSurface != this) {
|
||||
oldSurface._elementCache = null;
|
||||
}
|
||||
|
||||
if (dx != oldSurface.dx || dy != oldSurface.dy) {
|
||||
_applyTranslate();
|
||||
|
||||
@ -258,6 +258,42 @@ void testMain() {
|
||||
});
|
||||
});
|
||||
|
||||
/// Verify elementCache is passed during update to reuse existing
|
||||
/// image elements.
|
||||
test('Should retain same image element', () async {
|
||||
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
|
||||
final Picture picture1 = _drawPathImagePath();
|
||||
EngineLayer oldLayer = builder.pushClipRect(
|
||||
const Rect.fromLTRB(10, 10, 300, 300),
|
||||
);
|
||||
builder.addPicture(Offset.zero, picture1);
|
||||
builder.pop();
|
||||
|
||||
html.HtmlElement content = builder.build().webOnlyRootElement;
|
||||
html.document.body.append(content);
|
||||
List<html.ImageElement> list = content.querySelectorAll('img');
|
||||
for (html.ImageElement image in list) {
|
||||
image.alt = 'marked';
|
||||
}
|
||||
|
||||
// Force update to scene which will utilize reuse code path.
|
||||
final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder();
|
||||
builder2.pushClipRect(
|
||||
const Rect.fromLTRB(5, 10, 300, 300),
|
||||
oldLayer: oldLayer
|
||||
);
|
||||
final Picture picture2 = _drawPathImagePath();
|
||||
builder2.addPicture(Offset.zero, picture2);
|
||||
builder2.pop();
|
||||
|
||||
html.HtmlElement contentAfterReuse = builder2.build().webOnlyRootElement;
|
||||
list = contentAfterReuse.querySelectorAll('img');
|
||||
for (html.ImageElement image in list) {
|
||||
expect(image.alt, 'marked');
|
||||
}
|
||||
expect(list.length, 1);
|
||||
});
|
||||
|
||||
PersistedPicture findPictureSurfaceChild(PersistedContainerSurface parent) {
|
||||
PersistedPicture pictureSurface;
|
||||
parent.visitChildren((PersistedSurface child) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user