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.
This commit is contained in:
Jackson Gardner
2023-05-25 20:46:26 -07:00
committed by GitHub
parent d58d802fce
commit 387666e22f
27 changed files with 366 additions and 370 deletions

View File

@@ -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

View File

@@ -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,
<Object>[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 {}

View File

@@ -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<Object> 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<Object> ref) {

View File

@@ -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;

View File

@@ -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';

View File

@@ -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() {

View File

@@ -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<RawImageFilter> 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<RawImageFilter> _registry =
SkwasmFinalizationRegistry<RawImageFilter>(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<RawColorFilter> {
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<RawColorFilter> _registry =
SkwasmFinalizationRegistry<RawColorFilter>(colorFilterDispose);
}
class SkwasmMaskFilter {
SkwasmMaskFilter._(this.handle);
class SkwasmMaskFilter extends SkwasmObjectWrapper<RawMaskFilter> {
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<RawMaskFilter> _registry =
SkwasmFinalizationRegistry<RawMaskFilter>(maskFilterDispose);
}

View File

@@ -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<RawTypeface> {
SkwasmTypeface(SkDataHandle data) : super(typefaceCreate(data), _registry);
bool _isDisposed = false;
void dispose() {
if (!_isDisposed) {
_isDisposed = true;
typefaceDispose(handle);
}
}
TypefaceHandle handle;
static final SkwasmFinalizationRegistry<RawTypeface> _registry =
SkwasmFinalizationRegistry<RawTypeface>(typefaceDispose);
}
class SkwasmFontCollection implements FlutterFontCollection {
@@ -39,8 +31,13 @@ class SkwasmFontCollection implements FlutterFontCollection {
setDefaultFontFamilies(<String>['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<String, List<SkwasmTypeface>> registeredTypefaces = <String, List<SkwasmTypeface>>{};
void setDefaultFontFamilies(List<String> 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]);
}

View File

@@ -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<RawImage> 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<RawImage> _registry =
SkwasmFinalizationRegistry<RawImage>(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);

View File

@@ -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<T extends NativeType> {
SkwasmObjectWrapper(this.handle, this.registry) {
registry.register(this);
}
final SkwasmFinalizationRegistry<T> registry;
final Pointer<T> handle;
bool _isDisposed = false;
void dispose() {
assert(!_isDisposed);
registry.evict(this);
_isDisposed = true;
}
bool get debugDisposed => _isDisposed;
}
typedef DisposeFunction<T extends NativeType> = void Function(Pointer<T>);
class SkwasmFinalizationRegistry<T extends NativeType> {
SkwasmFinalizationRegistry(this.dispose)
: registry = DomFinalizationRegistry(((JSNumber address) =>
dispose(Pointer<T>.fromAddress(address.toDart.toInt()))
).toJS);
final DomFinalizationRegistry registry;
final DisposeFunction<T> dispose;
void register(SkwasmObjectWrapper<T> wrapper) {
registry.register(wrapper, wrapper.handle.address, wrapper);
}
void evict(SkwasmObjectWrapper<T> wrapper) {
registry.unregister(wrapper);
dispose(wrapper.handle);
}
}

View File

@@ -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<RawPaint> implements ui.Paint {
SkwasmPaint() : super(paintCreate(), _registry);
SkwasmPaint._fromHandle(this._handle);
PaintHandle _handle;
PaintHandle get handle => _handle;
static final SkwasmFinalizationRegistry<RawPaint> _registry =
SkwasmFinalizationRegistry<RawPaint>(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

View File

@@ -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<RawLineMetrics> 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<RawLineMetrics> _registry =
SkwasmFinalizationRegistry<RawLineMetrics>(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<RawParagraph> implements ui.Paragraph {
SkwasmParagraph(ParagraphHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawParagraph> _registry =
SkwasmFinalizationRegistry<RawParagraph>(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<RawTextStyle> {
SkwasmNativeTextStyle(TextStyleHandle handle) : super(handle, _registry);
factory SkwasmNativeTextStyle.defaultTextStyle() => SkwasmNativeTextStyle(textStyleCreate());
static final SkwasmFinalizationRegistry<RawTextStyle> _registry =
SkwasmFinalizationRegistry<RawTextStyle>(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<ui.FontVariation>? fontVariations;
}
class SkwasmStrutStyle implements ui.StrutStyle {
class SkwasmStrutStyle extends SkwasmObjectWrapper<RawStrutStyle> implements ui.StrutStyle {
factory SkwasmStrutStyle({
String? fontFamily,
List<String>? 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<RawStrutStyle> _registry =
SkwasmFinalizationRegistry<RawStrutStyle>(strutStyleDispose);
}
class SkwasmParagraphStyle implements ui.ParagraphStyle {
class SkwasmParagraphStyle extends SkwasmObjectWrapper<RawParagraphStyle> 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(<String>[fontFamily],
(Pointer<SkStringHandle> 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<RawParagraphStyle> _registry =
SkwasmFinalizationRegistry<RawParagraphStyle>(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<RawParagraphBuilder> 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<RawParagraphBuilder> _registry =
SkwasmFinalizationRegistry<RawParagraphBuilder>(paragraphBuilderDispose);
final SkwasmParagraphStyle style;
final List<_TextStyleStackEntry> textStyleStack = <_TextStyleStackEntry>[];
final List<SkwasmNativeTextStyle> textStyleStack = <SkwasmNativeTextStyle>[];
@override
List<double> placeholderScales = <double>[];
@@ -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);
}
}

View File

@@ -19,49 +19,45 @@ enum PathArcSize {
large,
}
class SkwasmPath implements ui.Path {
class SkwasmPath extends SkwasmObjectWrapper<RawPath> 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<RawPath> _registry =
SkwasmFinalizationRegistry<RawPath>(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<ui.Offset> 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<Float> 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}) {

View File

@@ -19,11 +19,13 @@ class SkwasmPathMetrics extends IterableBase<ui.PathMetric>
late Iterator<ui.PathMetric> iterator = SkwasmPathMetricIterator(path, forceClosed);
}
class SkwasmPathMetricIterator implements Iterator<ui.PathMetric> {
class SkwasmPathMetricIterator extends SkwasmObjectWrapper<RawContourMeasureIter> implements Iterator<ui.PathMetric> {
SkwasmPathMetricIterator(SkwasmPath path, bool forceClosed)
: _handle = contourMeasureIterCreate(path.handle, forceClosed, 1.0);
: super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry);
static final SkwasmFinalizationRegistry<RawContourMeasureIter> _registry =
SkwasmFinalizationRegistry<RawContourMeasureIter>(contourMeasureIterDispose);
final ContourMeasureIterHandle _handle;
SkwasmPathMetric? _current;
int _nextIndex = 0;
@@ -40,7 +42,7 @@ class SkwasmPathMetricIterator implements Iterator<ui.PathMetric> {
@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<ui.PathMetric> {
}
}
class SkwasmPathMetric implements ui.PathMetric {
SkwasmPathMetric(this._handle, this.contourIndex);
class SkwasmPathMetric extends SkwasmObjectWrapper<RawContourMeasure> implements ui.PathMetric {
SkwasmPathMetric(ContourMeasureHandle handle, this.contourIndex) : super(handle, _registry);
final ContourMeasureHandle _handle;
static final SkwasmFinalizationRegistry<RawContourMeasure> _registry =
SkwasmFinalizationRegistry<RawContourMeasure>(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<Float> outTangent =
Pointer<Float>.fromAddress(outPosition.address + sizeOf<Float>() * 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);
}

View File

@@ -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<RawPicture> implements ScenePicture {
SkwasmPicture.fromHandle(PictureHandle handle) : super(handle, _registry);
PictureHandle get handle => _handle;
static final SkwasmFinalizationRegistry<RawPicture> _registry =
SkwasmFinalizationRegistry<RawPicture>(pictureDispose);
@override
Future<ui.Image> 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<RawPictureRecorder> 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<RawPictureRecorder> _registry =
SkwasmFinalizationRegistry<RawPictureRecorder>(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;
}

View File

@@ -13,9 +13,6 @@ final class RawCanvas extends Opaque {}
typedef CanvasHandle = Pointer<RawCanvas>;
@Native<Void Function(CanvasHandle)>(symbol: 'canvas_destroy', isLeaf: true)
external void canvasDestroy(CanvasHandle canvas);
@Native<Void Function(CanvasHandle)>(symbol: 'canvas_save', isLeaf: true)
external void canvasSave(CanvasHandle canvas);

View File

@@ -16,8 +16,8 @@ typedef PaintHandle = Pointer<RawPaint>;
@Native<PaintHandle Function()>(symbol: 'paint_create', isLeaf: true)
external PaintHandle paintCreate();
@Native<Void Function(PaintHandle)>(symbol: 'paint_destroy', isLeaf: true)
external void paintDestroy(PaintHandle paint);
@Native<Void Function(PaintHandle)>(symbol: 'paint_dispose', isLeaf: true)
external void paintDispose(PaintHandle paint);
@Native<Void Function(PaintHandle, Int)>(symbol: 'paint_setBlendMode', isLeaf: true)
external void paintSetBlendMode(PaintHandle paint, int blendMode);

View File

@@ -16,8 +16,8 @@ typedef PathHandle = Pointer<RawPath>;
@Native<PathHandle Function()>(symbol: 'path_create', isLeaf: true)
external PathHandle pathCreate();
@Native<Void Function(PathHandle)>(symbol: 'path_destroy', isLeaf: true)
external void pathDestroy(PathHandle path);
@Native<Void Function(PathHandle)>(symbol: 'path_dispose', isLeaf: true)
external void pathDispose(PathHandle path);
@Native<PathHandle Function(PathHandle)>(symbol: 'path_copy', isLeaf: true)
external PathHandle pathCopy(PathHandle path);

View File

@@ -30,7 +30,7 @@ external ContourMeasureHandle contourMeasureIterNext(
symbol: 'contourMeasureIter_dispose')
external void contourMeasureIterDispose(ContourMeasureIterHandle handle);
@Native<Void Function(ContourMeasureHandle)>(symbol: 'contourMesaure_dispose')
@Native<Void Function(ContourMeasureHandle)>(symbol: 'contourMeasure_dispose')
external void contourMeasureDispose(ContourMeasureHandle handle);
@Native<Float Function(ContourMeasureHandle)>(symbol: 'contourMeasure_length')

View File

@@ -21,9 +21,9 @@ typedef PictureHandle = Pointer<RawPicture>;
external PictureRecorderHandle pictureRecorderCreate();
@Native<Void Function(PictureRecorderHandle)>(
symbol: 'pictureRecorder_destroy',
symbol: 'pictureRecorder_dispose',
isLeaf: true)
external void pictureRecorderDestroy(PictureRecorderHandle picture);
external void pictureRecorderDispose(PictureRecorderHandle picture);
@Native<CanvasHandle Function(PictureRecorderHandle, RawRect)>(
symbol: 'pictureRecorder_beginRecording',

View File

@@ -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);

View File

@@ -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<RawShader> implements SkwasmShader {
SkwasmNativeShader(ShaderHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawShader> _registry =
SkwasmFinalizationRegistry<RawShader>(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<RawRuntimeEffect> 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<RawRuntimeEffect> _registry =
SkwasmFinalizationRegistry<RawRuntimeEffect>(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<SkwasmShader?>.filled(program.childShaderCount, null);
class SkwasmShaderData extends SkwasmObjectWrapper<RawSkData> {
SkwasmShaderData(int size) : super(skDataCreate(size), _registry);
static final SkwasmFinalizationRegistry<RawSkData> _registry =
SkwasmFinalizationRegistry<RawSkData>(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<SkwasmShader?>.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<ShaderHandle> childShaders = nullptr;
if (_childShaders.isNotEmpty) {
childShaders = s.allocPointerArray(_childShaders.length)
.cast<ShaderHandle>();
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<SkwasmShader?> _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<Float> dataPointer = skDataGetPointer(_uniformData).cast<Float>();
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData.handle).cast<Float>();
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<Float> dataPointer = skDataGetPointer(_uniformData).cast<Float>();
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData.handle).cast<Float>();
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;
}
}
}

View File

@@ -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<RawVertices> implements ui.Vertices {
factory SkwasmVertices(
ui.VertexMode mode,
List<ui.Offset> 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<RawVertices> _registry =
SkwasmFinalizationRegistry<RawVertices>(verticesDispose);
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());
});
}