From 387666e22fe758efb65b72bf10e69025a13ae11f Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 25 May 2023 20:46:26 -0700 Subject: [PATCH] Proper memory management in Skwasm (flutter/engine#42328) This fixes https://github.com/flutter/flutter/issues/127243. This ensures that native skwasm objects are cleaned up when their associated dart-side objects are garbage collected. --- .../ci/licenses_golden/licenses_flutter | 2 + .../src/engine/canvaskit/canvaskit_api.dart | 39 ----- .../src/engine/canvaskit/native_memory.dart | 20 ++- .../lib/web_ui/lib/src/engine/dom.dart | 32 ++++ .../lib/src/engine/skwasm/skwasm_impl.dart | 1 + .../src/engine/skwasm/skwasm_impl/canvas.dart | 6 +- .../engine/skwasm/skwasm_impl/filters.dart | 45 ++---- .../skwasm/skwasm_impl/font_collection.dart | 27 ++-- .../src/engine/skwasm/skwasm_impl/image.dart | 28 ++-- .../src/engine/skwasm/skwasm_impl/memory.dart | 46 ++++++ .../src/engine/skwasm/skwasm_impl/paint.dart | 44 +++--- .../engine/skwasm/skwasm_impl/paragraph.dart | 125 +++++++-------- .../src/engine/skwasm/skwasm_impl/path.dart | 68 ++++----- .../skwasm/skwasm_impl/path_metrics.dart | 25 +-- .../engine/skwasm/skwasm_impl/picture.dart | 37 ++--- .../skwasm/skwasm_impl/raw/raw_canvas.dart | 3 - .../skwasm/skwasm_impl/raw/raw_paint.dart | 4 +- .../skwasm/skwasm_impl/raw/raw_path.dart | 4 +- .../skwasm_impl/raw/raw_path_metrics.dart | 2 +- .../skwasm/skwasm_impl/raw/raw_picture.dart | 4 +- .../engine/skwasm/skwasm_impl/renderer.dart | 1 - .../engine/skwasm/skwasm_impl/shaders.dart | 144 +++++++++--------- .../engine/skwasm/skwasm_impl/vertices.dart | 19 +-- .../lib/web_ui/skwasm/contour_measure.cpp | 4 + .../src/flutter/lib/web_ui/skwasm/paint.cpp | 2 +- engine/src/flutter/lib/web_ui/skwasm/path.cpp | 2 +- .../test/canvaskit/canvaskit_api_test.dart | 2 +- 27 files changed, 366 insertions(+), 370 deletions(-) create mode 100644 engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 23c6a80155..893d479e0d 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -2024,6 +2024,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.da ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart + ../../../flutter/LICENSE @@ -4682,6 +4683,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index dfc4820605..4aa608f4e2 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -3384,45 +3384,6 @@ extension JsConstructorExtension on JsConstructor { String get name => _name.toDart; } -/// Attaches a weakly referenced object to another object and calls a finalizer -/// with the latter when weakly referenced object is garbage collected. -/// -/// We use this to delete Skia objects when their "Ck" wrapper is garbage -/// collected. -/// -/// Example sequence of events: -/// -/// 1. A (CkPaint, SkPaint) pair created. -/// 2. The paint is used to paint some picture. -/// 3. CkPaint is dropped by the app. -/// 4. GC decides to perform a GC cycle and collects CkPaint. -/// 5. The finalizer function is called with the SkPaint as the sole argument. -/// 6. We call `delete` on SkPaint. -@JS('window.FinalizationRegistry') -@staticInterop -class SkObjectFinalizationRegistry {} - -SkObjectFinalizationRegistry createSkObjectFinalizationRegistry(JSFunction cleanup) { - return js_util.callConstructor( - _finalizationRegistryConstructor!.toObjectShallow, - [cleanup], - ); -} - -extension SkObjectFinalizationRegistryExtension on SkObjectFinalizationRegistry { - @JS('register') - external JSVoid _register(JSAny ckObject, JSAny skObject); - void register(Object ckObject, Object skObject) => - _register(ckObject.toJSAnyShallow, skObject.toJSAnyShallow); -} - -@JS('window.FinalizationRegistry') -external JSAny? get _finalizationRegistryConstructor; - -/// Whether the current browser supports `FinalizationRegistry`. -bool browserSupportsFinalizationRegistry = - _finalizationRegistryConstructor != null; - @JS() @staticInterop class SkData {} diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart index 35ef4bff1a..bf78968562 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart @@ -4,13 +4,23 @@ import 'dart:js_interop'; import 'package:meta/meta.dart'; - -import '../../engine.dart' show Instrumentation; -import 'canvaskit_api.dart'; +import 'package:ui/src/engine.dart'; /// Collects native objects that weren't explicitly disposed of using /// [UniqueRef.dispose] or [CountedRef.unref]. -SkObjectFinalizationRegistry _finalizationRegistry = createSkObjectFinalizationRegistry( +/// +/// We use this to delete Skia objects when their "Ck" wrapper is garbage +/// collected. +/// +/// Example sequence of events: +/// +/// 1. A (CkPaint, SkPaint) pair created. +/// 2. The paint is used to paint some picture. +/// 3. CkPaint is dropped by the app. +/// 4. GC decides to perform a GC cycle and collects CkPaint. +/// 5. The finalizer function is called with the SkPaint as the sole argument. +/// 6. We call `delete` on SkPaint. +DomFinalizationRegistry _finalizationRegistry = DomFinalizationRegistry( (UniqueRef uniq) { uniq.collect(); }.toJS @@ -18,7 +28,7 @@ SkObjectFinalizationRegistry _finalizationRegistry = createSkObjectFinalizationR NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry = NativeMemoryFinalizationRegistry(); -/// An indirection to [SkObjectFinalizationRegistry] to enable tests provide a +/// An indirection to [DomFinalizationRegistry] to enable tests provide a /// mock implementation of a finalization registry. class NativeMemoryFinalizationRegistry { void register(Object owner, UniqueRef ref) { diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart index 7e381fe5c4..45dc726999 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart @@ -3435,3 +3435,35 @@ class DomTextDecoder { extension DomTextDecoderExtension on DomTextDecoder { external JSString decode(JSTypedArray buffer); } + +@JS('window.FinalizationRegistry') +@staticInterop +class DomFinalizationRegistry { + external factory DomFinalizationRegistry(JSFunction cleanup); +} + +extension DomFinalizationRegistryExtension on DomFinalizationRegistry { + @JS('register') + external JSVoid _register1(JSAny target, JSAny value); + + @JS('register') + external JSVoid _register2(JSAny target, JSAny value, JSAny token); + void register(Object target, Object value, [Object? token]) { + if (token != null) { + _register2(target.toJSAnyShallow, value.toJSAnyShallow, token.toJSAnyShallow); + } else { + _register1(target.toJSAnyShallow, value.toJSAnyShallow); + } + } + + @JS('unregister') + external JSVoid _unregister(JSAny token); + void unregister(Object token) => _unregister(token.toJSAnyShallow); +} + +@JS('window.FinalizationRegistry') +external JSAny? get _finalizationRegistryConstructor; + +/// Whether the current browser supports `FinalizationRegistry`. +bool browserSupportsFinalizationRegistry = + _finalizationRegistryConstructor != null; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart index cb59b9ed13..ab0b58d728 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart @@ -15,6 +15,7 @@ export 'skwasm_impl/filters.dart'; export 'skwasm_impl/font_collection.dart'; export 'skwasm_impl/image.dart'; export 'skwasm_impl/layers.dart'; +export 'skwasm_impl/memory.dart'; export 'skwasm_impl/paint.dart'; export 'skwasm_impl/paragraph.dart'; export 'skwasm_impl/path.dart'; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart index 4eb409ba25..0e331835f7 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart @@ -18,9 +18,9 @@ class SkwasmCanvas implements SceneCanvas { SkwasmCanvas.fromHandle(this._handle); CanvasHandle _handle; - void delete() { - canvasDestroy(_handle); - } + // Note that we do not need to deal with the finalizer registry here, because + // the underlying native skia object is tied directly to the lifetime of the + // associated SkPictureRecorder. @override void save() { diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart index acd8711378..adc8026ef1 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart @@ -9,8 +9,8 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmImageFilter implements SceneImageFilter { - SkwasmImageFilter._(this.handle); +class SkwasmImageFilter extends SkwasmObjectWrapper implements SceneImageFilter { + SkwasmImageFilter._(ImageFilterHandle handle) : super(handle, _registry); factory SkwasmImageFilter.blur({ double sigmaX = 0.0, @@ -60,15 +60,8 @@ class SkwasmImageFilter implements SceneImageFilter { return SkwasmImageFilter._(imageFilterCompose(nativeOuter.handle, nativeInner.handle)); } - void dispose() { - if (!_isDisposed) { - imageFilterDispose(handle); - _isDisposed = true; - } - } - - final ImageFilterHandle handle; - bool _isDisposed = false; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(imageFilterDispose); @override ui.Rect filterBounds(ui.Rect inputBounds) => withStackScope((StackScope scope) { @@ -78,8 +71,8 @@ class SkwasmImageFilter implements SceneImageFilter { }); } -class SkwasmColorFilter { - SkwasmColorFilter._(this.handle); +class SkwasmColorFilter extends SkwasmObjectWrapper { + SkwasmColorFilter._(ColorFilterHandle handle) : super(handle, _registry); factory SkwasmColorFilter.fromEngineColorFilter(EngineColorFilter colorFilter) => switch (colorFilter.type) { @@ -100,19 +93,12 @@ class SkwasmColorFilter { SkwasmColorFilter inner, ) => SkwasmColorFilter._(colorFilterCompose(outer.handle, inner.handle)); - void dispose() { - if (!_isDisposed) { - colorFilterDispose(handle); - _isDisposed = true; - } - } - - final ColorFilterHandle handle; - bool _isDisposed = false; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(colorFilterDispose); } -class SkwasmMaskFilter { - SkwasmMaskFilter._(this.handle); +class SkwasmMaskFilter extends SkwasmObjectWrapper { + SkwasmMaskFilter._(MaskFilterHandle handle) : super(handle, _registry); factory SkwasmMaskFilter.fromUiMaskFilter(ui.MaskFilter maskFilter) => SkwasmMaskFilter._(maskFilterCreateBlur( @@ -120,13 +106,6 @@ class SkwasmMaskFilter { maskFilter.webOnlySigma )); - final MaskFilterHandle handle; - bool _isDisposed = false; - - void dispose() { - if (!_isDisposed) { - maskFilterDispose(handle); - _isDisposed = true; - } - } + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(maskFilterDispose); } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart index 605598ba17..e71b5eb785 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart @@ -19,19 +19,11 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; const String _robotoUrl = 'https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf'; -class SkwasmTypeface { - SkwasmTypeface(SkDataHandle data) : handle = typefaceCreate(data); +class SkwasmTypeface extends SkwasmObjectWrapper { + SkwasmTypeface(SkDataHandle data) : super(typefaceCreate(data), _registry); - bool _isDisposed = false; - - void dispose() { - if (!_isDisposed) { - _isDisposed = true; - typefaceDispose(handle); - } - } - - TypefaceHandle handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(typefaceDispose); } class SkwasmFontCollection implements FlutterFontCollection { @@ -39,8 +31,13 @@ class SkwasmFontCollection implements FlutterFontCollection { setDefaultFontFamilies(['Roboto']); } + // Most of the time, when an object deals with native handles to skwasm objects, + // we register it with a finalization registry so that it can clean up the handle + // when the dart side of object gets GC'd. However, this object is basically a + // singleton (the renderer creates one and just hangs onto it forever) so it's + // not really worth it to do the finalization dance here. FontCollectionHandle handle = fontCollectionCreate(); - TextStyleHandle defaultTextStyle = textStyleCreate(); + SkwasmNativeTextStyle defaultTextStyle = SkwasmNativeTextStyle.defaultTextStyle(); final Map> registeredTypefaces = >{}; void setDefaultFontFamilies(List families) => withStackScope((StackScope scope) { @@ -49,8 +46,8 @@ class SkwasmFontCollection implements FlutterFontCollection { for (int i = 0; i < families.length; i++) { familyPointers[i] = skStringFromDartString(families[i]); } - textStyleClearFontFamilies(defaultTextStyle); - textStyleAddFontFamilies(defaultTextStyle, familyPointers, families.length); + textStyleClearFontFamilies(defaultTextStyle.handle); + textStyleAddFontFamilies(defaultTextStyle.handle, familyPointers, families.length); for (int i = 0; i < families.length; i++) { skStringFree(familyPointers[i]); } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart index e0f3c76c8f..a3940ab788 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart @@ -9,8 +9,11 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmImage implements ui.Image { - SkwasmImage(this.handle); +class SkwasmImage extends SkwasmObjectWrapper implements ui.Image { + SkwasmImage(ImageHandle handle) : super(handle, _registry) + { + ui.Image.onCreate?.call(this); + } factory SkwasmImage.fromPixels( Uint8List pixels, @@ -35,8 +38,14 @@ class SkwasmImage implements ui.Image { return SkwasmImage(imageHandle); } - final ImageHandle handle; - bool _isDisposed = false; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(imageDispose); + + @override + void dispose() { + super.dispose(); + ui.Image.onDispose?.call(this); + } @override int get width => imageGetWidth(handle); @@ -53,17 +62,6 @@ class SkwasmImage implements ui.Image { @override ui.ColorSpace get colorSpace => ui.ColorSpace.sRGB; - @override - void dispose() { - if (!_isDisposed) { - imageDispose(handle); - _isDisposed = true; - } - } - - @override - bool get debugDisposed => _isDisposed; - @override SkwasmImage clone() { imageRef(handle); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart new file mode 100644 index 0000000000..4b0f04cc3b --- /dev/null +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'dart:js_interop'; + +import 'package:ui/src/engine.dart'; + +class SkwasmObjectWrapper { + SkwasmObjectWrapper(this.handle, this.registry) { + registry.register(this); + } + final SkwasmFinalizationRegistry registry; + final Pointer handle; + bool _isDisposed = false; + + void dispose() { + assert(!_isDisposed); + registry.evict(this); + _isDisposed = true; + } + + bool get debugDisposed => _isDisposed; +} + +typedef DisposeFunction = void Function(Pointer); + +class SkwasmFinalizationRegistry { + SkwasmFinalizationRegistry(this.dispose) + : registry = DomFinalizationRegistry(((JSNumber address) => + dispose(Pointer.fromAddress(address.toDart.toInt())) + ).toJS); + + final DomFinalizationRegistry registry; + final DisposeFunction dispose; + + void register(SkwasmObjectWrapper wrapper) { + registry.register(wrapper, wrapper.handle.address, wrapper); + } + + void evict(SkwasmObjectWrapper wrapper) { + registry.unregister(wrapper); + dispose(wrapper.handle); + } +} diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart index ad889e0712..a813ce9483 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart @@ -8,15 +8,11 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmPaint implements ui.Paint { - factory SkwasmPaint() { - return SkwasmPaint._fromHandle(paintCreate()); - } +class SkwasmPaint extends SkwasmObjectWrapper implements ui.Paint { + SkwasmPaint() : super(paintCreate(), _registry); - SkwasmPaint._fromHandle(this._handle); - PaintHandle _handle; - - PaintHandle get handle => _handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(paintDispose); ui.BlendMode _cachedBlendMode = ui.BlendMode.srcOver; @@ -47,51 +43,51 @@ class SkwasmPaint implements ui.Paint { set blendMode(ui.BlendMode blendMode) { if (_cachedBlendMode != blendMode) { _cachedBlendMode = blendMode; - paintSetBlendMode(_handle, blendMode.index); + paintSetBlendMode(handle, blendMode.index); } } @override - ui.PaintingStyle get style => ui.PaintingStyle.values[paintGetStyle(_handle)]; + ui.PaintingStyle get style => ui.PaintingStyle.values[paintGetStyle(handle)]; @override - set style(ui.PaintingStyle style) => paintSetStyle(_handle, style.index); + set style(ui.PaintingStyle style) => paintSetStyle(handle, style.index); @override - double get strokeWidth => paintGetStrokeWidth(_handle); + double get strokeWidth => paintGetStrokeWidth(handle); @override - set strokeWidth(double width) => paintSetStrokeWidth(_handle, width); + set strokeWidth(double width) => paintSetStrokeWidth(handle, width); @override - ui.StrokeCap get strokeCap => ui.StrokeCap.values[paintGetStrokeCap(_handle)]; + ui.StrokeCap get strokeCap => ui.StrokeCap.values[paintGetStrokeCap(handle)]; @override - set strokeCap(ui.StrokeCap cap) => paintSetStrokeCap(_handle, cap.index); + set strokeCap(ui.StrokeCap cap) => paintSetStrokeCap(handle, cap.index); @override - ui.StrokeJoin get strokeJoin => ui.StrokeJoin.values[paintGetStrokeJoin(_handle)]; + ui.StrokeJoin get strokeJoin => ui.StrokeJoin.values[paintGetStrokeJoin(handle)]; @override - set strokeJoin(ui.StrokeJoin join) => paintSetStrokeJoin(_handle, join.index); + set strokeJoin(ui.StrokeJoin join) => paintSetStrokeJoin(handle, join.index); @override - bool get isAntiAlias => paintGetAntiAlias(_handle); + bool get isAntiAlias => paintGetAntiAlias(handle); @override - set isAntiAlias(bool value) => paintSetAntiAlias(_handle, value); + set isAntiAlias(bool value) => paintSetAntiAlias(handle, value); @override - ui.Color get color => ui.Color(paintGetColorInt(_handle)); + ui.Color get color => ui.Color(paintGetColorInt(handle)); @override - set color(ui.Color color) => paintSetColorInt(_handle, color.value); + set color(ui.Color color) => paintSetColorInt(handle, color.value); @override - double get strokeMiterLimit => paintGetMiterLimit(_handle); + double get strokeMiterLimit => paintGetMiterLimit(handle); @override - set strokeMiterLimit(double limit) => paintSetMiterLimit(_handle, limit); + set strokeMiterLimit(double limit) => paintSetMiterLimit(handle, limit); @override ui.Shader? get shader => _shader; @@ -102,7 +98,7 @@ class SkwasmPaint implements ui.Paint { _shader = skwasmShader; final ShaderHandle shaderHandle = skwasmShader != null ? skwasmShader.handle : nullptr; - paintSetShader(_handle, shaderHandle); + paintSetShader(handle, shaderHandle); } @override diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart index d2f8e5f65c..c00c1bcac9 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -13,7 +13,7 @@ import 'package:ui/ui.dart' as ui; const int _kSoftLineBreak = 0; const int _kHardLineBreak = 100; -class SkwasmLineMetrics implements ui.LineMetrics { +class SkwasmLineMetrics extends SkwasmObjectWrapper implements ui.LineMetrics { factory SkwasmLineMetrics({ required bool hardBreak, required double ascent, @@ -36,10 +36,10 @@ class SkwasmLineMetrics implements ui.LineMetrics { lineNumber, )); - SkwasmLineMetrics._(this.handle); + SkwasmLineMetrics._(LineMetricsHandle handle) : super(handle, _registry); - final LineMetricsHandle handle; - bool _isDisposed = false; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(lineMetricsDispose); @override bool get hardBreak => lineMetricsGetHardBreak(handle); @@ -67,20 +67,14 @@ class SkwasmLineMetrics implements ui.LineMetrics { @override int get lineNumber => lineMetricsGetLineNumber(handle); - - void dispose() { - if (_isDisposed) { - lineMetricsDispose(handle); - _isDisposed = true; - } - } } -class SkwasmParagraph implements ui.Paragraph { - SkwasmParagraph(this.handle); +class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Paragraph { + SkwasmParagraph(ParagraphHandle handle) : super(handle, _registry); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(paragraphDispose); - final ParagraphHandle handle; - bool _isDisposed = false; bool _hasCheckedForMissingCodePoints = false; @override @@ -220,17 +214,6 @@ class SkwasmParagraph implements ui.Paragraph { (int index) => SkwasmLineMetrics._(paragraphGetLineMetricsAtIndex(handle, index)) ); } - - @override - bool get debugDisposed => _isDisposed; - - @override - void dispose() { - if (!_isDisposed) { - paragraphDispose(handle); - _isDisposed = true; - } - } } void withScopedFontList( @@ -251,6 +234,19 @@ void withScopedFontList( }); } +class SkwasmNativeTextStyle extends SkwasmObjectWrapper { + SkwasmNativeTextStyle(TextStyleHandle handle) : super(handle, _registry); + + factory SkwasmNativeTextStyle.defaultTextStyle() => SkwasmNativeTextStyle(textStyleCreate()); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(textStyleDispose); + + SkwasmNativeTextStyle copy() { + return SkwasmNativeTextStyle(textStyleCopy(handle)); + } +} + class SkwasmTextStyle implements ui.TextStyle { SkwasmTextStyle({ this.color, @@ -276,7 +272,8 @@ class SkwasmTextStyle implements ui.TextStyle { this.fontVariations, }); - void applyToHandle(TextStyleHandle handle) { + void applyToNative(SkwasmNativeTextStyle style) { + final TextStyleHandle handle = style.handle; if (color != null) { textStyleSetColor(handle, color!.value); } @@ -409,7 +406,7 @@ class SkwasmTextStyle implements ui.TextStyle { final List? fontVariations; } -class SkwasmStrutStyle implements ui.StrutStyle { +class SkwasmStrutStyle extends SkwasmObjectWrapper implements ui.StrutStyle { factory SkwasmStrutStyle({ String? fontFamily, List? fontFamilyFallback, @@ -458,12 +455,13 @@ class SkwasmStrutStyle implements ui.StrutStyle { return SkwasmStrutStyle._(handle); } - SkwasmStrutStyle._(this.handle); + SkwasmStrutStyle._(StrutStyleHandle handle) : super(handle, _registry); - final StrutStyleHandle handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(strutStyleDispose); } -class SkwasmParagraphStyle implements ui.ParagraphStyle { +class SkwasmParagraphStyle extends SkwasmObjectWrapper implements ui.ParagraphStyle { factory SkwasmParagraphStyle({ ui.TextAlign? textAlign, ui.TextDirection? textDirection, @@ -507,9 +505,10 @@ class SkwasmParagraphStyle implements ui.ParagraphStyle { strutStyle as SkwasmStrutStyle; paragraphStyleSetStrutStyle(handle, strutStyle.handle); } - final TextStyleHandle textStyleHandle = textStyleCopy( - (renderer.fontCollection as SkwasmFontCollection).defaultTextStyle, - ); + final SkwasmNativeTextStyle textStyle = + (renderer.fontCollection as SkwasmFontCollection).defaultTextStyle.copy(); + final TextStyleHandle textStyleHandle = textStyle.handle; + if (fontFamily != null) { withScopedFontList([fontFamily], (Pointer families, int count) => @@ -536,24 +535,23 @@ class SkwasmParagraphStyle implements ui.ParagraphStyle { skStringFree(localeHandle); } paragraphStyleSetTextStyle(handle, textStyleHandle); - return SkwasmParagraphStyle._(handle, textStyleHandle, fontFamily); + return SkwasmParagraphStyle._(handle, textStyle, fontFamily); } - SkwasmParagraphStyle._(this.handle, this.textStyleHandle, this.defaultFontFamily); + SkwasmParagraphStyle._( + ParagraphStyleHandle handle, + this.textStyle, + this.defaultFontFamily + ) : super(handle, _registry); - final ParagraphStyleHandle handle; - final TextStyleHandle textStyleHandle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(paragraphStyleDispose); + + final SkwasmNativeTextStyle textStyle; final String? defaultFontFamily; } -class _TextStyleStackEntry { - _TextStyleStackEntry(this.style, this.handle); - - SkwasmTextStyle style; - TextStyleHandle handle; -} - -class SkwasmParagraphBuilder implements ui.ParagraphBuilder { +class SkwasmParagraphBuilder extends SkwasmObjectWrapper implements ui.ParagraphBuilder { factory SkwasmParagraphBuilder( SkwasmParagraphStyle style, SkwasmFontCollection collection, @@ -562,10 +560,13 @@ class SkwasmParagraphBuilder implements ui.ParagraphBuilder { collection.handle, ), style); - SkwasmParagraphBuilder._(this.handle, this.style); - final ParagraphBuilderHandle handle; + SkwasmParagraphBuilder._(ParagraphBuilderHandle handle, this.style) : super(handle, _registry); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(paragraphBuilderDispose); + final SkwasmParagraphStyle style; - final List<_TextStyleStackEntry> textStyleStack = <_TextStyleStackEntry>[]; + final List textStyleStack = []; @override List placeholderScales = []; @@ -695,28 +696,20 @@ class SkwasmParagraphBuilder implements ui.ParagraphBuilder { @override void pop() { - final TextStyleHandle textStyleHandle = textStyleStack.removeLast().handle; - textStyleDispose(textStyleHandle); + final SkwasmNativeTextStyle style = textStyleStack.removeLast(); + style.dispose(); paragraphBuilderPop(handle); } @override void pushStyle(ui.TextStyle textStyle) { textStyle as SkwasmTextStyle; - TextStyleHandle sourceStyleHandle = nullptr; - if (textStyleStack.isNotEmpty) { - sourceStyleHandle = textStyleStack.last.handle; - } - if (sourceStyleHandle == nullptr) { - sourceStyleHandle = style.textStyleHandle; - } - if (sourceStyleHandle == nullptr) { - sourceStyleHandle = - (renderer.fontCollection as SkwasmFontCollection).defaultTextStyle; - } - final TextStyleHandle styleHandle = textStyleCopy(sourceStyleHandle); - textStyle.applyToHandle(styleHandle); - textStyleStack.add(_TextStyleStackEntry(textStyle, styleHandle)); - paragraphBuilderPushStyle(handle, styleHandle); + final SkwasmNativeTextStyle baseStyle = textStyleStack.isNotEmpty + ? textStyleStack.last + : style.textStyle; + final SkwasmNativeTextStyle nativeStyle = baseStyle.copy(); + textStyle.applyToNative(nativeStyle); + textStyleStack.add(nativeStyle); + paragraphBuilderPushStyle(handle, nativeStyle.handle); } } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart index 78a2e9f004..1a95219620 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart @@ -19,49 +19,45 @@ enum PathArcSize { large, } -class SkwasmPath implements ui.Path { +class SkwasmPath extends SkwasmObjectWrapper implements ui.Path { factory SkwasmPath() { return SkwasmPath.fromHandle(pathCreate()); } factory SkwasmPath.from(SkwasmPath source) { - return SkwasmPath.fromHandle(pathCopy(source._handle)); + return SkwasmPath.fromHandle(pathCopy(source.handle)); } - SkwasmPath.fromHandle(this._handle); - final PathHandle _handle; + SkwasmPath.fromHandle(PathHandle handle) : super(handle, _registry); - PathHandle get handle => _handle; - - void delete() { - pathDestroy(_handle); - } + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(pathDispose); @override - ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(_handle)]; + ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(handle)]; @override - set fillType(ui.PathFillType fillType) => pathSetFillType(_handle, fillType.index); + set fillType(ui.PathFillType fillType) => pathSetFillType(handle, fillType.index); @override - void moveTo(double x, double y) => pathMoveTo(_handle, x, y); + void moveTo(double x, double y) => pathMoveTo(handle, x, y); @override - void relativeMoveTo(double x, double y) => pathRelativeMoveTo(_handle, x, y); + void relativeMoveTo(double x, double y) => pathRelativeMoveTo(handle, x, y); @override - void lineTo(double x, double y) => pathLineTo(_handle, x, y); + void lineTo(double x, double y) => pathLineTo(handle, x, y); @override - void relativeLineTo(double x, double y) => pathRelativeMoveTo(_handle, x, y); + void relativeLineTo(double x, double y) => pathRelativeMoveTo(handle, x, y); @override void quadraticBezierTo(double x1, double y1, double x2, double y2) => - pathQuadraticBezierTo(_handle, x1, y1, x2, y2); + pathQuadraticBezierTo(handle, x1, y1, x2, y2); @override void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) => - pathRelativeQuadraticBezierTo(_handle, x1, y1, x2, y2); + pathRelativeQuadraticBezierTo(handle, x1, y1, x2, y2); @override void cubicTo( @@ -71,7 +67,7 @@ class SkwasmPath implements ui.Path { double y2, double x3, double y3) => - pathCubicTo(_handle, x1, y1, x2, y2, x3, y3); + pathCubicTo(handle, x1, y1, x2, y2, x3, y3); @override void relativeCubicTo( @@ -81,22 +77,22 @@ class SkwasmPath implements ui.Path { double y2, double x3, double y3) => - pathRelativeCubicTo(_handle, x1, y1, x2, y2, x3, y3); + pathRelativeCubicTo(handle, x1, y1, x2, y2, x3, y3); @override void conicTo(double x1, double y1, double x2, double y2, double w) => - pathConicTo(_handle, x1, y1, x2, y2, w); + pathConicTo(handle, x1, y1, x2, y2, w); @override void relativeConicTo(double x1, double y1, double x2, double y2, double w) => - pathRelativeConicTo(_handle, x1, y1, x2, y2, w); + pathRelativeConicTo(handle, x1, y1, x2, y2, w); @override void arcTo( ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { withStackScope((StackScope s) { pathArcToOval( - _handle, + handle, s.convertRectToNative(rect), ui.toDegrees(startAngle), ui.toDegrees(sweepAngle), @@ -118,7 +114,7 @@ class SkwasmPath implements ui.Path { final PathDirection pathDirection = clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; pathArcToRotated( - _handle, + handle, radius.x, radius.y, ui.toDegrees(rotation), @@ -142,7 +138,7 @@ class SkwasmPath implements ui.Path { final PathDirection pathDirection = clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; pathRelativeArcToRotated( - _handle, + handle, radius.x, radius.y, ui.toDegrees(rotation), @@ -156,14 +152,14 @@ class SkwasmPath implements ui.Path { @override void addRect(ui.Rect rect) { withStackScope((StackScope s) { - pathAddRect(_handle, s.convertRectToNative(rect)); + pathAddRect(handle, s.convertRectToNative(rect)); }); } @override void addOval(ui.Rect rect) { withStackScope((StackScope s) { - pathAddOval(_handle, s.convertRectToNative(rect)); + pathAddOval(handle, s.convertRectToNative(rect)); }); } @@ -171,7 +167,7 @@ class SkwasmPath implements ui.Path { void addArc(ui.Rect rect, double startAngle, double sweepAngle) { withStackScope((StackScope s) { pathAddArc( - _handle, + handle, s.convertRectToNative(rect), ui.toDegrees(startAngle), ui.toDegrees(sweepAngle) @@ -182,14 +178,14 @@ class SkwasmPath implements ui.Path { @override void addPolygon(List points, bool close) { withStackScope((StackScope s) { - pathAddPolygon(_handle, s.convertPointArrayToNative(points), points.length, close); + pathAddPolygon(handle, s.convertPointArrayToNative(points), points.length, close); }); } @override void addRRect(ui.RRect rrect) { withStackScope((StackScope s) { - pathAddRRect(_handle, s.convertRRectToNative(rrect)); + pathAddRRect(handle, s.convertRRectToNative(rrect)); }); } @@ -210,18 +206,18 @@ class SkwasmPath implements ui.Path { s.convertMatrix4toSkMatrix(matrix4 ?? Matrix4.identity().toFloat64()); convertedMatrix[2] += offset.dx; convertedMatrix[5] += offset.dy; - pathAddPath(_handle, (path as SkwasmPath)._handle, convertedMatrix, extend); + pathAddPath(handle, (path as SkwasmPath).handle, convertedMatrix, extend); }); } @override - void close() => pathClose(_handle); + void close() => pathClose(handle); @override - void reset() => pathReset(_handle); + void reset() => pathReset(handle); @override - bool contains(ui.Offset point) => pathContains(_handle, point.dx, point.dy); + bool contains(ui.Offset point) => pathContains(handle, point.dx, point.dy); @override ui.Path shift(ui.Offset offset) => @@ -230,7 +226,7 @@ class SkwasmPath implements ui.Path { @override ui.Path transform(Float64List matrix4) { return withStackScope((StackScope s) { - final PathHandle newPathHandle = pathCopy(_handle); + final PathHandle newPathHandle = pathCopy(handle); pathTransform(newPathHandle, s.convertMatrix4toSkMatrix(matrix4)); return SkwasmPath.fromHandle(newPathHandle); }); @@ -240,7 +236,7 @@ class SkwasmPath implements ui.Path { ui.Rect getBounds() { return withStackScope((StackScope s) { final Pointer rectBuffer = s.allocFloatArray(4); - pathGetBounds(_handle, rectBuffer); + pathGetBounds(handle, rectBuffer); return s.convertRectFromNative(rectBuffer); }); } @@ -250,7 +246,7 @@ class SkwasmPath implements ui.Path { SkwasmPath path1, SkwasmPath path2) => SkwasmPath.fromHandle(pathCombine( - operation.index, path1._handle, path2._handle)); + operation.index, path1.handle, path2.handle)); @override ui.PathMetrics computeMetrics({bool forceClosed = false}) { diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart index da753adbc9..d6b558b356 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart @@ -19,11 +19,13 @@ class SkwasmPathMetrics extends IterableBase late Iterator iterator = SkwasmPathMetricIterator(path, forceClosed); } -class SkwasmPathMetricIterator implements Iterator { +class SkwasmPathMetricIterator extends SkwasmObjectWrapper implements Iterator { SkwasmPathMetricIterator(SkwasmPath path, bool forceClosed) - : _handle = contourMeasureIterCreate(path.handle, forceClosed, 1.0); + : super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(contourMeasureIterDispose); - final ContourMeasureIterHandle _handle; SkwasmPathMetric? _current; int _nextIndex = 0; @@ -40,7 +42,7 @@ class SkwasmPathMetricIterator implements Iterator { @override bool moveNext() { - final ContourMeasureHandle measureHandle = contourMeasureIterNext(_handle); + final ContourMeasureHandle measureHandle = contourMeasureIterNext(handle); if (measureHandle == nullptr) { _current = null; return false; @@ -52,10 +54,11 @@ class SkwasmPathMetricIterator implements Iterator { } } -class SkwasmPathMetric implements ui.PathMetric { - SkwasmPathMetric(this._handle, this.contourIndex); +class SkwasmPathMetric extends SkwasmObjectWrapper implements ui.PathMetric { + SkwasmPathMetric(ContourMeasureHandle handle, this.contourIndex) : super(handle, _registry); - final ContourMeasureHandle _handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(contourMeasureDispose); @override final int contourIndex; @@ -63,7 +66,7 @@ class SkwasmPathMetric implements ui.PathMetric { @override ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { return SkwasmPath.fromHandle( - contourMeasureGetSegment(_handle, start, end, startWithMoveTo)); + contourMeasureGetSegment(handle, start, end, startWithMoveTo)); } @override @@ -73,7 +76,7 @@ class SkwasmPathMetric implements ui.PathMetric { final Pointer outTangent = Pointer.fromAddress(outPosition.address + sizeOf() * 2); final bool result = - contourMeasureGetPosTan(_handle, distance, outPosition, outTangent); + contourMeasureGetPosTan(handle, distance, outPosition, outTangent); assert(result); return ui.Tangent( ui.Offset(outPosition[0], outPosition[1]), @@ -83,8 +86,8 @@ class SkwasmPathMetric implements ui.PathMetric { } @override - bool get isClosed => contourMeasureIsClosed(_handle); + bool get isClosed => contourMeasureIsClosed(handle); @override - double get length => contourMeasureLength(_handle); + double get length => contourMeasureLength(handle); } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart index 9e404b6557..887cbffc53 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart @@ -6,28 +6,24 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmPicture implements ScenePicture { - SkwasmPicture.fromHandle(this._handle); - final PictureHandle _handle; +class SkwasmPicture extends SkwasmObjectWrapper implements ScenePicture { + SkwasmPicture.fromHandle(PictureHandle handle) : super(handle, _registry); - PictureHandle get handle => _handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(pictureDispose); @override Future toImage(int width, int height) async => toImageSync(width, height); + @override + int get approximateBytesUsed => pictureApproximateBytesUsed(handle); + @override void dispose() { + super.dispose(); ui.Picture.onDispose?.call(this); - pictureDispose(_handle); - debugDisposed = true; } - @override - int get approximateBytesUsed => pictureApproximateBytesUsed(_handle); - - @override - bool debugDisposed = false; - @override ui.Image toImageSync(int width, int height) => SkwasmImage(imageCreateFromPicture(handle, width, height)); @@ -42,22 +38,19 @@ class SkwasmPicture implements ScenePicture { } } -class SkwasmPictureRecorder implements ui.PictureRecorder { - factory SkwasmPictureRecorder() => - SkwasmPictureRecorder._fromHandle(pictureRecorderCreate()); +class SkwasmPictureRecorder extends SkwasmObjectWrapper implements ui.PictureRecorder { + SkwasmPictureRecorder() : super(pictureRecorderCreate(), _registry); - SkwasmPictureRecorder._fromHandle(this._handle); - final PictureRecorderHandle _handle; - - PictureRecorderHandle get handle => _handle; - - void delete() => pictureRecorderDestroy(_handle); + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(pictureRecorderDispose); @override SkwasmPicture endRecording() { isRecording = false; - final SkwasmPicture picture = SkwasmPicture.fromHandle(pictureRecorderEndRecording(_handle)); + final SkwasmPicture picture = SkwasmPicture.fromHandle( + pictureRecorderEndRecording(handle) + ); ui.Picture.onCreate?.call(picture); return picture; } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart index 67637dd158..599085ea6c 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart @@ -13,9 +13,6 @@ final class RawCanvas extends Opaque {} typedef CanvasHandle = Pointer; -@Native(symbol: 'canvas_destroy', isLeaf: true) -external void canvasDestroy(CanvasHandle canvas); - @Native(symbol: 'canvas_save', isLeaf: true) external void canvasSave(CanvasHandle canvas); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart index a0372b0ad9..307e6968d5 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart @@ -16,8 +16,8 @@ typedef PaintHandle = Pointer; @Native(symbol: 'paint_create', isLeaf: true) external PaintHandle paintCreate(); -@Native(symbol: 'paint_destroy', isLeaf: true) -external void paintDestroy(PaintHandle paint); +@Native(symbol: 'paint_dispose', isLeaf: true) +external void paintDispose(PaintHandle paint); @Native(symbol: 'paint_setBlendMode', isLeaf: true) external void paintSetBlendMode(PaintHandle paint, int blendMode); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart index 0674eb829c..4a4108a108 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart @@ -16,8 +16,8 @@ typedef PathHandle = Pointer; @Native(symbol: 'path_create', isLeaf: true) external PathHandle pathCreate(); -@Native(symbol: 'path_destroy', isLeaf: true) -external void pathDestroy(PathHandle path); +@Native(symbol: 'path_dispose', isLeaf: true) +external void pathDispose(PathHandle path); @Native(symbol: 'path_copy', isLeaf: true) external PathHandle pathCopy(PathHandle path); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart index e3422387c1..01705325ce 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart @@ -30,7 +30,7 @@ external ContourMeasureHandle contourMeasureIterNext( symbol: 'contourMeasureIter_dispose') external void contourMeasureIterDispose(ContourMeasureIterHandle handle); -@Native(symbol: 'contourMesaure_dispose') +@Native(symbol: 'contourMeasure_dispose') external void contourMeasureDispose(ContourMeasureHandle handle); @Native(symbol: 'contourMeasure_length') diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart index 004499bdd0..a1499068df 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart @@ -21,9 +21,9 @@ typedef PictureHandle = Pointer; external PictureRecorderHandle pictureRecorderCreate(); @Native( - symbol: 'pictureRecorder_destroy', + symbol: 'pictureRecorder_dispose', isLeaf: true) -external void pictureRecorderDestroy(PictureRecorderHandle picture); +external void pictureRecorderDispose(PictureRecorderHandle picture); @Native( symbol: 'pictureRecorder_beginRecording', diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 4a0018acea..46ec0fe541 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -24,7 +24,6 @@ class SkwasmRenderer implements Renderer { return SkwasmPath.combine(op, path1 as SkwasmPath, path2 as SkwasmPath); } - @override ui.Path copyPath(ui.Path src) { return SkwasmPath.from(src as SkwasmPath); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart index 08a867e13f..e4eb5f1935 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart @@ -10,21 +10,21 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; +// A shared interface for shaders for which you can acquire a native handle abstract class SkwasmShader implements ui.Shader { ShaderHandle get handle; - - @override - bool get debugDisposed => handle == nullptr; - - @override - void dispose() { - if (handle != nullptr) { - shaderDispose(handle); - } - } } -class SkwasmGradient extends SkwasmShader implements ui.Gradient { +// An implementation that handles the storage, disposal, and finalization of +// a native shader handle. +class SkwasmNativeShader extends SkwasmObjectWrapper implements SkwasmShader { + SkwasmNativeShader(ShaderHandle handle) : super(handle, _registry); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(shaderDispose); +} + +class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { factory SkwasmGradient.linear({ required ui.Offset from, required ui.Offset to, @@ -143,20 +143,11 @@ class SkwasmGradient extends SkwasmShader implements ui.Gradient { return SkwasmGradient._(handle); }); - SkwasmGradient._(this.handle); - - @override - ShaderHandle handle; - - @override - void dispose() { - super.dispose(); - handle = nullptr; - } + SkwasmGradient._(super.handle); } -class SkwasmImageShader extends SkwasmShader implements ui.ImageShader { - SkwasmImageShader._(this.handle); +class SkwasmImageShader extends SkwasmNativeShader implements ui.ImageShader { + SkwasmImageShader._(super.handle); factory SkwasmImageShader.imageShader( SkwasmImage image, @@ -186,24 +177,16 @@ class SkwasmImageShader extends SkwasmShader implements ui.ImageShader { )); } } - - @override - ShaderHandle handle; - - @override - void dispose() { - super.dispose(); - handle = nullptr; - } } -class SkwasmFragmentProgram implements ui.FragmentProgram { +class SkwasmFragmentProgram extends SkwasmObjectWrapper implements ui.FragmentProgram { SkwasmFragmentProgram._( this.name, - this.handle, + RuntimeEffectHandle handle, this.floatUniformCount, this.childShaderCount, - ); + ) : super(handle, _registry); + factory SkwasmFragmentProgram.fromBytes(String name, Uint8List bytes) { final ShaderData shaderData = ShaderData.fromBytes(bytes); @@ -226,76 +209,102 @@ class SkwasmFragmentProgram implements ui.FragmentProgram { ); } - final RuntimeEffectHandle handle; + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(runtimeEffectDispose); + final String name; final int floatUniformCount; final int childShaderCount; - bool _isDisposed = false; @override ui.FragmentShader fragmentShader() => SkwasmFragmentShader(this); int get uniformSize => runtimeEffectGetUniformSize(handle); - - void dispose() { - if (!_isDisposed) { - runtimeEffectDispose(handle); - _isDisposed = true; - } - } } -class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { - SkwasmFragmentShader( - SkwasmFragmentProgram program - ) : _program = program, - _uniformData = skDataCreate(program.uniformSize), - _floatUniformCount = program.floatUniformCount, - _childShaders = List.filled(program.childShaderCount, null); +class SkwasmShaderData extends SkwasmObjectWrapper { + SkwasmShaderData(int size) : super(skDataCreate(size), _registry); + + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(skDataDispose); +} + +// This class does not inherit from SkwasmNativeShader, as its handle might +// change over time if the uniforms or image shaders are changed. Instead this +// wraps a SkwasmNativeShader that it creates and destroys on demand. It does +// implement SkwasmShader though, in order to provide the handle for the +// underlying shader object. +class SkwasmFragmentShader implements SkwasmShader, ui.FragmentShader { + SkwasmFragmentShader(SkwasmFragmentProgram program) : + _program = program, + _uniformData = SkwasmShaderData(program.uniformSize), + _floatUniformCount = program.floatUniformCount, + _childShaders = List.filled(program.childShaderCount, null); @override ShaderHandle get handle { - if (_handle == nullptr) { - _handle = withStackScope((StackScope s) { + if (_nativeShader == null) { + final ShaderHandle newHandle = withStackScope((StackScope s) { Pointer childShaders = nullptr; if (_childShaders.isNotEmpty) { childShaders = s.allocPointerArray(_childShaders.length) .cast(); for (int i = 0; i < _childShaders.length; i++) { - childShaders[i] = _childShaders[i]!.handle; + final SkwasmShader? child = _childShaders[i]; + childShaders[i] = child != null ? child.handle : nullptr; } } return shaderCreateRuntimeEffectShader( _program.handle, - _uniformData, + _uniformData.handle, childShaders, _childShaders.length, ); }); + _nativeShader = SkwasmNativeShader(newHandle); } - return _handle; + return _nativeShader!.handle; } - ShaderHandle _handle = nullptr; + SkwasmShader? _nativeShader; final SkwasmFragmentProgram _program; - SkDataHandle _uniformData; + final SkwasmShaderData _uniformData; + bool _isDisposed = false; final int _floatUniformCount; final List _childShaders; + @override + void dispose() { + assert(!_isDisposed); + _nativeShader?.dispose(); + _uniformData.dispose(); + _isDisposed = true; + } + + @override + bool get debugDisposed => _isDisposed; + @override void setFloat(int index, double value) { - if (_handle != nullptr) { + if (_nativeShader != null) { // Invalidate the previous shader so that it is recreated with the new // uniform data. - shaderDispose(_handle); - _handle = nullptr; + _nativeShader!.dispose(); + _nativeShader = null; } - final Pointer dataPointer = skDataGetPointer(_uniformData).cast(); + final Pointer dataPointer = skDataGetPointer(_uniformData.handle).cast(); dataPointer[index] = value; } @override void setImageSampler(int index, ui.Image image) { + if (_nativeShader != null) { + // Invalidate the previous shader so that it is recreated with the new + // child shaders. + _nativeShader!.dispose(); + _nativeShader = null; + } + final SkwasmImageShader shader = SkwasmImageShader.imageShader( image as SkwasmImage, ui.TileMode.clamp, @@ -307,17 +316,8 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { _childShaders[index] = shader; oldShader?.dispose(); - final Pointer dataPointer = skDataGetPointer(_uniformData).cast(); + final Pointer dataPointer = skDataGetPointer(_uniformData.handle).cast(); dataPointer[_floatUniformCount + index * 2] = image.width.toDouble(); dataPointer[_floatUniformCount + index * 2 + 1] = image.height.toDouble(); } - - @override - void dispose() { - super.dispose(); - if (_uniformData != nullptr) { - skDataDispose(_uniformData); - _uniformData = nullptr; - } - } } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart index 8f26227316..e4d5142c0e 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart @@ -10,7 +10,7 @@ import 'dart:typed_data'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmVertices implements ui.Vertices { +class SkwasmVertices extends SkwasmObjectWrapper implements ui.Vertices { factory SkwasmVertices( ui.VertexMode mode, List positions, { @@ -69,19 +69,8 @@ class SkwasmVertices implements ui.Vertices { )); }); - SkwasmVertices._(this.handle); + SkwasmVertices._(VerticesHandle handle) : super(handle, _registry); - final VerticesHandle handle; - bool _isDisposed = false; - - @override - bool get debugDisposed => _isDisposed; - - @override - void dispose() { - if (!_isDisposed) { - verticesDispose(handle); - _isDisposed = true; - } - } + static final SkwasmFinalizationRegistry _registry = + SkwasmFinalizationRegistry(verticesDispose); } diff --git a/engine/src/flutter/lib/web_ui/skwasm/contour_measure.cpp b/engine/src/flutter/lib/web_ui/skwasm/contour_measure.cpp index 642d1fa369..1e732420e0 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/contour_measure.cpp +++ b/engine/src/flutter/lib/web_ui/skwasm/contour_measure.cpp @@ -23,6 +23,10 @@ SKWASM_EXPORT SkContourMeasure* contourMeasureIter_next( return next.get(); } +SKWASM_EXPORT void contourMeasureIter_dispose(SkContourMeasureIter* iter) { + delete iter; +} + SKWASM_EXPORT void contourMeasure_dispose(SkContourMeasure* measure) { measure->unref(); } diff --git a/engine/src/flutter/lib/web_ui/skwasm/paint.cpp b/engine/src/flutter/lib/web_ui/skwasm/paint.cpp index 62fc4d04f1..d75264dad4 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/paint.cpp +++ b/engine/src/flutter/lib/web_ui/skwasm/paint.cpp @@ -20,7 +20,7 @@ SKWASM_EXPORT SkPaint* paint_create() { return paint; } -SKWASM_EXPORT void paint_destroy(SkPaint* paint) { +SKWASM_EXPORT void paint_dispose(SkPaint* paint) { delete paint; } diff --git a/engine/src/flutter/lib/web_ui/skwasm/path.cpp b/engine/src/flutter/lib/web_ui/skwasm/path.cpp index 63690f1370..e0092bf612 100644 --- a/engine/src/flutter/lib/web_ui/skwasm/path.cpp +++ b/engine/src/flutter/lib/web_ui/skwasm/path.cpp @@ -13,7 +13,7 @@ SKWASM_EXPORT SkPath* path_create() { return new SkPath(); } -SKWASM_EXPORT void path_destroy(SkPath* path) { +SKWASM_EXPORT void path_dispose(SkPath* path) { delete path; } diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index d6a7b9238f..ed918d4614 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -1907,7 +1907,7 @@ void _paragraphTests() { // FinalizationRegistry because it depends on GC, which cannot be controlled, // So the test simply tests that a FinalizationRegistry can be constructed // and its `register` method can be called. - final SkObjectFinalizationRegistry registry = createSkObjectFinalizationRegistry((String arg) {}.toJS); + final DomFinalizationRegistry registry = DomFinalizationRegistry((String arg) {}.toJS); registry.register(Object(), Object()); }); }