mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Dispose the things (flutter/engine#35730)
This commit is contained in:
parent
d1a925947b
commit
6ae82e899c
@ -197,6 +197,7 @@ typedef CanvasPath Path;
|
||||
V(Paragraph, alphabeticBaseline, 1) \
|
||||
V(Paragraph, computeLineMetrics, 1) \
|
||||
V(Paragraph, didExceedMaxLines, 1) \
|
||||
V(Paragraph, dispose, 1) \
|
||||
V(Paragraph, getLineBoundary, 2) \
|
||||
V(Paragraph, getPositionForOffset, 3) \
|
||||
V(Paragraph, getRectsForPlaceholders, 1) \
|
||||
@ -280,7 +281,8 @@ typedef CanvasPath Path;
|
||||
V(SemanticsUpdateBuilder, build, 2) \
|
||||
V(SemanticsUpdateBuilder, updateCustomAction, 5) \
|
||||
V(SemanticsUpdateBuilder, updateNode, 36) \
|
||||
V(SemanticsUpdate, dispose, 1)
|
||||
V(SemanticsUpdate, dispose, 1) \
|
||||
V(Vertices, dispose, 1)
|
||||
|
||||
#define FFI_FUNCTION_INSERT(FUNCTION, ARGS) \
|
||||
g_function_dispatchers.insert(std::make_pair( \
|
||||
|
||||
@ -4417,6 +4417,36 @@ class Vertices extends NativeFieldWrapperClass1 {
|
||||
Float32List? textureCoordinates,
|
||||
Int32List? colors,
|
||||
Uint16List? indices);
|
||||
|
||||
/// Release the resources used by this object. The object is no longer usable
|
||||
/// after this method is called.
|
||||
void dispose() {
|
||||
assert(!_disposed);
|
||||
assert(() {
|
||||
_disposed = true;
|
||||
return true;
|
||||
}());
|
||||
_dispose();
|
||||
}
|
||||
|
||||
/// This can't be a leaf call because the native function calls Dart API
|
||||
/// (Dart_SetNativeInstanceField).
|
||||
@FfiNative<Void Function(Pointer<Void>)>('Vertices::dispose')
|
||||
external void _dispose();
|
||||
|
||||
bool _disposed = false;
|
||||
/// Whether this reference to the underlying picture is [dispose]d.
|
||||
///
|
||||
/// This only returns a valid value if asserts are enabled, and must not be
|
||||
/// used otherwise.
|
||||
bool get debugDisposed {
|
||||
bool? disposed;
|
||||
assert(() {
|
||||
disposed = _disposed;
|
||||
return true;
|
||||
}());
|
||||
return disposed ?? (throw StateError('$runtimeType.debugDisposed is only available when asserts are enabled.'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines how a list of points is interpreted when drawing a set of points.
|
||||
@ -5009,6 +5039,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// given [Offset]. The image is composited into the canvas using the given [Paint].
|
||||
void drawImage(Image image, Offset offset, Paint paint) {
|
||||
assert(image != null); // image is checked on the engine side
|
||||
assert(!image.debugDisposed);
|
||||
assert(_offsetIsValid(offset));
|
||||
assert(paint != null);
|
||||
final String? error = _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index);
|
||||
@ -5031,6 +5062,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// performance.
|
||||
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
|
||||
assert(image != null); // image is checked on the engine side
|
||||
assert(!image.debugDisposed);
|
||||
assert(_rectIsValid(src));
|
||||
assert(_rectIsValid(dst));
|
||||
assert(paint != null);
|
||||
@ -5081,6 +5113,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// positions.
|
||||
void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
|
||||
assert(image != null); // image is checked on the engine side
|
||||
assert(!image.debugDisposed);
|
||||
assert(_rectIsValid(center));
|
||||
assert(_rectIsValid(dst));
|
||||
assert(paint != null);
|
||||
@ -5120,6 +5153,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// [PictureRecorder].
|
||||
void drawPicture(Picture picture) {
|
||||
assert(picture != null); // picture is checked on the engine side
|
||||
assert(!picture.debugDisposed);
|
||||
_drawPicture(picture);
|
||||
}
|
||||
|
||||
@ -5148,6 +5182,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate.
|
||||
void drawParagraph(Paragraph paragraph, Offset offset) {
|
||||
assert(paragraph != null);
|
||||
assert(!paragraph.debugDisposed);
|
||||
assert(_offsetIsValid(offset));
|
||||
assert(!paragraph._needsLayout);
|
||||
paragraph._paint(this, offset.dx, offset.dy);
|
||||
@ -5211,8 +5246,8 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
/// rather than unencoded lists.
|
||||
/// * [paint], Image shaders can be used to draw images on a triangular mesh.
|
||||
void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) {
|
||||
|
||||
assert(vertices != null); // vertices is checked on the engine side
|
||||
assert(!vertices.debugDisposed);
|
||||
assert(paint != null);
|
||||
assert(blendMode != null);
|
||||
_drawVertices(vertices, blendMode.index, paint._objects, paint._data);
|
||||
@ -5360,6 +5395,7 @@ class Canvas extends NativeFieldWrapperClass1 {
|
||||
Rect? cullRect,
|
||||
Paint paint) {
|
||||
assert(atlas != null); // atlas is checked on the engine side
|
||||
assert(!atlas.debugDisposed);
|
||||
assert(transforms != null);
|
||||
assert(rects != null);
|
||||
assert(colors == null || colors.isEmpty || blendMode != null);
|
||||
|
||||
@ -81,7 +81,15 @@ bool Vertices::init(Dart_Handle vertices_handle,
|
||||
}
|
||||
|
||||
size_t Vertices::GetAllocationSize() const {
|
||||
if (!vertices_) {
|
||||
return sizeof(*this);
|
||||
}
|
||||
return vertices_->size();
|
||||
}
|
||||
|
||||
void Vertices::dispose() {
|
||||
vertices_.reset();
|
||||
ClearDartWrapper();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -30,6 +30,8 @@ class Vertices : public RefCountedDartWrappable<Vertices> {
|
||||
|
||||
size_t GetAllocationSize() const override;
|
||||
|
||||
void dispose();
|
||||
|
||||
private:
|
||||
Vertices();
|
||||
|
||||
|
||||
@ -1017,6 +1017,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass1 {
|
||||
///
|
||||
/// The returned object can be passed to [PlatformDispatcher.updateSemantics]
|
||||
/// to actually update the semantics retained by the system.
|
||||
///
|
||||
/// This object is unusable after calling build.
|
||||
SemanticsUpdate build() {
|
||||
final SemanticsUpdate semanticsUpdate = SemanticsUpdate._();
|
||||
_build(semanticsUpdate);
|
||||
|
||||
@ -131,6 +131,7 @@ void SemanticsUpdateBuilder::updateCustomAction(int id,
|
||||
void SemanticsUpdateBuilder::build(Dart_Handle semantics_update_handle) {
|
||||
SemanticsUpdate::create(semantics_update_handle, std::move(nodes_),
|
||||
std::move(actions_));
|
||||
ClearDartWrapper();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -2907,6 +2907,36 @@ class Paragraph extends NativeFieldWrapperClass1 {
|
||||
|
||||
@FfiNative<Handle Function(Pointer<Void>)>('Paragraph::computeLineMetrics')
|
||||
external Float64List _computeLineMetrics();
|
||||
|
||||
/// Release the resources used by this object. The object is no longer usable
|
||||
/// after this method is called.
|
||||
void dispose() {
|
||||
assert(!_disposed);
|
||||
assert(() {
|
||||
_disposed = true;
|
||||
return true;
|
||||
}());
|
||||
_dispose();
|
||||
}
|
||||
|
||||
/// This can't be a leaf call because the native function calls Dart API
|
||||
/// (Dart_SetNativeInstanceField).
|
||||
@FfiNative<Void Function(Pointer<Void>)>('Paragraph::dispose')
|
||||
external void _dispose();
|
||||
|
||||
bool _disposed = false;
|
||||
/// Whether this reference to the underlying picture is [dispose]d.
|
||||
///
|
||||
/// This only returns a valid value if asserts are enabled, and must not be
|
||||
/// used otherwise.
|
||||
bool get debugDisposed {
|
||||
bool? disposed;
|
||||
assert(() {
|
||||
disposed = _disposed;
|
||||
return true;
|
||||
}());
|
||||
return disposed ?? (throw StateError('$runtimeType.debugDisposed is only available when asserts are enabled.'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a [Paragraph] containing text with the given styling information.
|
||||
@ -3166,8 +3196,8 @@ class ParagraphBuilder extends NativeFieldWrapperClass1 {
|
||||
_placeholderScales.add(scale);
|
||||
}
|
||||
|
||||
@FfiNative<Handle Function(Pointer<Void>, Double, Double, Uint32, Double, Uint32)>('ParagraphBuilder::addPlaceholder')
|
||||
external String? _addPlaceholder(double width, double height, int alignment, double baselineOffset, int baseline);
|
||||
@FfiNative<Void Function(Pointer<Void>, Double, Double, Uint32, Double, Uint32)>('ParagraphBuilder::addPlaceholder')
|
||||
external void _addPlaceholder(double width, double height, int alignment, double baselineOffset, int baseline);
|
||||
|
||||
/// Applies the given paragraph style and returns a [Paragraph] containing the
|
||||
/// added text and associated styling.
|
||||
|
||||
@ -66,6 +66,11 @@ void Paragraph::layout(double width) {
|
||||
}
|
||||
|
||||
void Paragraph::paint(Canvas* canvas, double x, double y) {
|
||||
if (!m_paragraph || !canvas) {
|
||||
// disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
SkCanvas* sk_canvas = canvas->canvas();
|
||||
if (!sk_canvas) {
|
||||
return;
|
||||
@ -168,4 +173,9 @@ tonic::Float64List Paragraph::computeLineMetrics() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Paragraph::dispose() {
|
||||
m_paragraph.reset();
|
||||
ClearDartWrapper();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -51,6 +51,8 @@ class Paragraph : public RefCountedDartWrappable<Paragraph> {
|
||||
|
||||
size_t GetAllocationSize() const override;
|
||||
|
||||
void dispose();
|
||||
|
||||
private:
|
||||
std::unique_ptr<txt::Paragraph> m_paragraph;
|
||||
|
||||
|
||||
@ -528,22 +528,22 @@ Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
Dart_Handle ParagraphBuilder::addPlaceholder(double width,
|
||||
double height,
|
||||
unsigned alignment,
|
||||
double baseline_offset,
|
||||
unsigned baseline) {
|
||||
void ParagraphBuilder::addPlaceholder(double width,
|
||||
double height,
|
||||
unsigned alignment,
|
||||
double baseline_offset,
|
||||
unsigned baseline) {
|
||||
txt::PlaceholderRun placeholder_run(
|
||||
width, height, static_cast<txt::PlaceholderAlignment>(alignment),
|
||||
static_cast<txt::TextBaseline>(baseline), baseline_offset);
|
||||
|
||||
m_paragraphBuilder->AddPlaceholder(placeholder_run);
|
||||
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
void ParagraphBuilder::build(Dart_Handle paragraph_handle) {
|
||||
Paragraph::Create(paragraph_handle, m_paragraphBuilder->Build());
|
||||
m_paragraphBuilder.reset();
|
||||
ClearDartWrapper();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -60,11 +60,11 @@ class ParagraphBuilder : public RefCountedDartWrappable<ParagraphBuilder> {
|
||||
// Internally, this method adds a single object replacement character (0xFFFC)
|
||||
// and emplaces a new PlaceholderRun instance to the vector of inline
|
||||
// placeholders.
|
||||
Dart_Handle addPlaceholder(double width,
|
||||
double height,
|
||||
unsigned alignment,
|
||||
double baseline_offset,
|
||||
unsigned baseline);
|
||||
void addPlaceholder(double width,
|
||||
double height,
|
||||
unsigned alignment,
|
||||
double baseline_offset,
|
||||
unsigned baseline);
|
||||
|
||||
void build(Dart_Handle paragraph_handle);
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ enum VertexMode {
|
||||
triangleFan,
|
||||
}
|
||||
|
||||
class Vertices {
|
||||
abstract class Vertices {
|
||||
factory Vertices(
|
||||
VertexMode mode,
|
||||
List<Offset> positions, {
|
||||
@ -48,6 +48,9 @@ class Vertices {
|
||||
colors: colors,
|
||||
indices: indices);
|
||||
}
|
||||
|
||||
void dispose();
|
||||
bool get debugDisposed;
|
||||
}
|
||||
|
||||
abstract class PictureRecorder {
|
||||
|
||||
@ -797,6 +797,23 @@ class CkParagraph extends SkiaObject<SkParagraph> implements ui.Paragraph {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
delete();
|
||||
didDelete();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Paragraph.debugDisposed is only available when asserts are enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
class CkLineMetrics implements ui.LineMetrics {
|
||||
|
||||
@ -6,6 +6,7 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../util.dart';
|
||||
import 'canvaskit_api.dart';
|
||||
import 'skia_object_cache.dart';
|
||||
|
||||
@ -108,4 +109,20 @@ class CkVertices extends ManagedSkiaObject<SkVertices> implements ui.Vertices {
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
delete();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Vertices.debugDisposed is only avialalbe when asserts are enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +57,21 @@ class SurfaceVertices implements ui.Vertices {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Vertices.debugDisposed is only avialalbe when asserts are enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Lazily initializes web gl.
|
||||
|
||||
@ -8,6 +8,7 @@ import '../dom.dart';
|
||||
import '../embedder.dart';
|
||||
import '../html/bitmap_canvas.dart';
|
||||
import '../profiler.dart';
|
||||
import '../util.dart';
|
||||
import 'layout_service.dart';
|
||||
import 'paint_service.dart';
|
||||
import 'paragraph.dart';
|
||||
@ -40,7 +41,7 @@ class CanvasParagraph implements ui.Paragraph {
|
||||
final EngineParagraphStyle paragraphStyle;
|
||||
|
||||
/// The full textual content of the paragraph.
|
||||
final String plainText;
|
||||
late String plainText;
|
||||
|
||||
/// The number of placeholders in this paragraph.
|
||||
final int placeholderCount;
|
||||
@ -254,6 +255,23 @@ class CanvasParagraph implements ui.Paragraph {
|
||||
List<EngineLineMetrics> computeLineMetrics() {
|
||||
return lines.map((ParagraphLine line) => line.lineMetrics).toList();
|
||||
}
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
spans.clear();
|
||||
plainText = '';
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Vertices.debugDisposed is only avialalbe when asserts are enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
void _positionSpanElement(DomElement element, ParagraphLine line, RangeBox box) {
|
||||
|
||||
@ -670,6 +670,8 @@ abstract class Paragraph {
|
||||
TextRange getLineBoundary(TextPosition position);
|
||||
List<TextBox> getBoxesForPlaceholders();
|
||||
List<LineMetrics> computeLineMetrics();
|
||||
void dispose();
|
||||
bool get debugDisposed;
|
||||
}
|
||||
|
||||
abstract class ParagraphBuilder {
|
||||
|
||||
@ -109,6 +109,7 @@ void testNoCrashes() {
|
||||
testCanvas((Canvas canvas) => canvas.translate(double.nan, double.nan));
|
||||
testCanvas((Canvas canvas) => canvas.drawVertices(Vertices(VertexMode.triangles, <Offset>[],
|
||||
indices: <int>[]), BlendMode.screen, paint));
|
||||
testCanvas((Canvas canvas) => canvas.drawVertices(Vertices(VertexMode.triangles, <Offset>[])..dispose(), BlendMode.screen, paint));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ void main() {
|
||||
final ParagraphBuilder paragraphBuilder =
|
||||
ParagraphBuilder(ParagraphStyle());
|
||||
paragraphBuilder.build();
|
||||
paragraphBuilder.pushStyle(TextStyle());
|
||||
expect(() { paragraphBuilder.pushStyle(TextStyle()); }, throwsStateError);
|
||||
});
|
||||
|
||||
test('GetRectsForRange smoke test', () {
|
||||
|
||||
@ -197,4 +197,19 @@ void main() {
|
||||
expect(line.start, 6);
|
||||
expect(line.end, 10);
|
||||
});
|
||||
|
||||
test('painting a disposed paragraph does not crash', () {
|
||||
final Paragraph paragraph = ParagraphBuilder(ParagraphStyle()).build();
|
||||
paragraph.dispose();
|
||||
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder);
|
||||
|
||||
void callback() { canvas.drawParagraph(paragraph, Offset.zero); }
|
||||
if (assertStatementsEnabled) {
|
||||
expectAssertion(callback);
|
||||
} else {
|
||||
expect(callback, throwsStateError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user