diff --git a/packages/flutter/lib/src/painting/debug.dart b/packages/flutter/lib/src/painting/debug.dart index 25ef095b2c8..c8fbcbba9a7 100644 --- a/packages/flutter/lib/src/painting/debug.dart +++ b/packages/flutter/lib/src/painting/debug.dart @@ -4,7 +4,7 @@ import 'dart:io'; -import 'dart:ui' show Size, hashValues; +import 'dart:ui' show Size, hashValues, Picture, Image; import 'package:flutter/foundation.dart'; @@ -195,3 +195,29 @@ bool debugAssertAllPaintingVarsUnset(String reason, { bool debugDisableShadowsOv }()); return true; } + +/// The signature of [debugCaptureShaderWarmUpPicture]. +/// +/// Used by tests to run assertions on the [Picture] created by +/// [ShaderWarmUp.execute]. The return value indicates whether the assertions +/// pass or not. +typedef ShaderWarmUpPictureCallback = bool Function(Picture); + +/// The signature of [debugCaptureShaderWarmUpImage]. +/// +/// Used by tests to run assertions on the [Image] created by +/// [ShaderWarmUp.execute]. The return value indicates whether the assertions +/// pass or not. +typedef ShaderWarmUpImageCallback = bool Function(Image); + +/// Called by [ShaderWarmUp.execute] immediately after it creates a [Picture]. +/// +/// Tests may use this to capture the picture and run assertions on it. +ShaderWarmUpPictureCallback debugCaptureShaderWarmUpPicture = _defaultPictureCapture; +bool _defaultPictureCapture(Picture picture) => true; + +/// Called by [ShaderWarmUp.execute] immediately after it creates an [Image]. +/// +/// Tests may use this to capture the picture and run assertions on it. +ShaderWarmUpImageCallback debugCaptureShaderWarmUpImage = _defaultImageCapture; +bool _defaultImageCapture(Image image) => true; diff --git a/packages/flutter/lib/src/painting/shader_warm_up.dart b/packages/flutter/lib/src/painting/shader_warm_up.dart index b6921bf1460..57ce493a194 100644 --- a/packages/flutter/lib/src/painting/shader_warm_up.dart +++ b/packages/flutter/lib/src/painting/shader_warm_up.dart @@ -7,6 +7,8 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'debug.dart'; + /// Interface for drawing an image to warm up Skia shader compilations. /// /// When Skia first sees a certain type of draw operation on the GPU, it needs @@ -85,15 +87,19 @@ abstract class ShaderWarmUp { final ui.Canvas canvas = ui.Canvas(recorder); await warmUpOnCanvas(canvas); final ui.Picture picture = recorder.endRecording(); + assert(debugCaptureShaderWarmUpPicture(picture)); if (!kIsWeb) { // Picture.toImage is not yet implemented on the web. final TimelineTask shaderWarmUpTask = TimelineTask(); shaderWarmUpTask.start('Warm-up shader'); try { - await picture.toImage(size.width.ceil(), size.height.ceil()); + final ui.Image image = await picture.toImage(size.width.ceil(), size.height.ceil()); + assert(debugCaptureShaderWarmUpImage(image)); + image.dispose(); } finally { shaderWarmUpTask.finish(); } } + picture.dispose(); } } diff --git a/packages/flutter/test/painting/shader_warm_up_test.dart b/packages/flutter/test/painting/shader_warm_up_test.dart index 849f67292fb..54e2d7b305d 100644 --- a/packages/flutter/test/painting/shader_warm_up_test.dart +++ b/packages/flutter/test/painting/shader_warm_up_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -32,4 +35,27 @@ void main() { expect(hasDrawRectAfterClipRRect, true); }); + + test('ShaderWarmUp.execute disposes the image and picture', () async { + const DefaultShaderWarmUp shaderWarmUp = DefaultShaderWarmUp(); + late ui.Picture capturedPicture; + late ui.Image capturedImage; + debugCaptureShaderWarmUpPicture = (ui.Picture picture) { + capturedPicture = picture; + expect(picture.approximateBytesUsed, greaterThan(0)); + return true; + }; + debugCaptureShaderWarmUpImage = (ui.Image image) { + capturedImage = image; + expect(image.width, 100); + expect(image.height, 100); + return true; + }; + await shaderWarmUp.execute(); + expect( + () => capturedPicture.approximateBytesUsed, + throwsA(isA().having((String message) => message, 'message', 'Object has been disposed.')), + ); + expect(capturedImage.debugDisposed, true); + }, skip: kIsWeb); // Browser doesn't support approximateBytesUsed and doesn't rasterize the picture at this time. }