[flutter] Proper disposal of native resources.

This commit is contained in:
Mario Zechner 2022-08-25 17:33:35 +02:00
parent ac03e51d0b
commit 2438f0c39e
7 changed files with 94 additions and 43 deletions

View File

@ -5,7 +5,6 @@ class ExampleSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
const spacer = SizedBox(height: 10);
return Scaffold(
appBar: AppBar(title: const Text('Spine Examples')),
body: Center(
@ -48,6 +47,6 @@ class Spineboy extends StatelessWidget {
void main() {
runApp(MaterialApp(
title: "Spine Examples",
home: Spineboy()
home: ExampleSelector()
));
}

View File

@ -12,13 +12,15 @@ import 'package:path/path.dart' as Path;
int majorVersion() => _bindings.spine_major_version();
int minorVersion() => _bindings.spine_minor_version();
void reportLeaks() => _bindings.spine_report_leaks();
class SpineAtlas {
Pointer<spine_atlas> _atlas;
List<Image> atlasPages;
List<Paint> atlasPagePaints;
bool _disposed;
SpineAtlas(this._atlas, this.atlasPages, this.atlasPagePaints);
SpineAtlas(this._atlas, this.atlasPages, this.atlasPagePaints): _disposed = false;
static Future<SpineAtlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
final atlasData = await assetBundle.loadString(atlasFileName);
@ -51,12 +53,20 @@ class SpineAtlas {
return SpineAtlas(atlas, atlasPages, atlasPagePaints);
}
void dispose() {
if (_disposed) return;
_disposed = true;
_bindings.spine_atlas_dispose(this._atlas);
for (final image in atlasPages) image.dispose();
}
}
class SpineSkeletonData {
Pointer<spine_skeleton_data> _skeletonData;
bool _disposed;
SpineSkeletonData(this._skeletonData);
SpineSkeletonData(this._skeletonData): _disposed = false;
static SpineSkeletonData fromJson(SpineAtlas atlas, String json) {
final jsonNative = json.toNativeUtf8();
@ -83,22 +93,31 @@ class SpineSkeletonData {
}
return SpineSkeletonData(skeletonData);
}
void dispose() {
if (_disposed) return;
_disposed = true;
_bindings.spine_skeleton_data_dispose(this._skeletonData);
}
}
class SpineSkeletonDrawable {
SpineAtlas atlas;
SpineSkeletonData skeletonData;
late Pointer<spine_skeleton_drawable> _drawable;
bool _disposed;
SpineSkeletonDrawable(this.atlas, this.skeletonData) {
SpineSkeletonDrawable(this.atlas, this.skeletonData): _disposed = false {
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData);
}
void update(double delta) {
if (_disposed) return;
_bindings.spine_skeleton_drawable_update(_drawable, delta);
}
List<SpineRenderCommand> render() {
if (_disposed) return [];
Pointer<spine_render_command> nativeCmd = _bindings.spine_skeleton_drawable_render(_drawable);
List<SpineRenderCommand> commands = [];
while(nativeCmd.address != nullptr.address) {
@ -108,6 +127,14 @@ class SpineSkeletonDrawable {
}
return commands;
}
void dispose() {
if (_disposed) return;
_disposed = true;
atlas.dispose();
skeletonData.dispose();
_bindings.spine_skeleton_drawable_dispose(_drawable);
}
}
class SpineRenderCommand {

View File

@ -44,6 +44,15 @@ class SpineFlutterBindings {
late final _spine_minor_version =
_spine_minor_versionPtr.asFunction<int Function()>();
void spine_report_leaks() {
return _spine_report_leaks();
}
late final _spine_report_leaksPtr =
_lookup<ffi.NativeFunction<ffi.Void Function()>>('spine_report_leaks');
late final _spine_report_leaks =
_spine_report_leaksPtr.asFunction<void Function()>();
ffi.Pointer<spine_atlas> spine_atlas_load(
ffi.Pointer<ffi.Int8> atlasData,
) {
@ -254,5 +263,7 @@ class spine_skeleton_drawable extends ffi.Struct {
external ffi.Pointer<ffi.Void> animationState;
external ffi.Pointer<ffi.Void> clipping;
external ffi.Pointer<spine_render_command> renderCommand;
}

View File

@ -47,6 +47,12 @@ class _SpineWidgetState extends State<SpineWidget> {
return SizedBox();
}
}
@override
void dispose() {
skeletonDrawable?.dispose();
super.dispose();
}
}
class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
@ -76,7 +82,6 @@ class _SpineRenderObject extends RenderBox {
set skeletonDrawable(SpineSkeletonDrawable skeletonDrawable) {
if (_skeletonDrawable == skeletonDrawable) return;
// FIXME dispose old drawable here?
_skeletonDrawable = skeletonDrawable;
markNeedsPaint();
}

View File

@ -18,6 +18,4 @@ set_target_properties(spine_flutter PROPERTIES
)
target_include_directories(spine_flutter PUBLIC spine-cpp/include)
target_compile_definitions(spine_flutter PUBLIC DART_SHARED_LIB)
#target_compile_options(spine_flutter PUBLIC -fsanitize=address -fno-omit-frame-pointer)
#set_target_properties(spine_flutter PROPERTIES LINK_FLAGS -fsanitize=address)

View File

@ -1,6 +1,7 @@
#include "spine_flutter.h"
#include <spine/spine.h>
#include <spine/Version.h>
#include <spine/Debug.h>
using namespace spine;
@ -26,6 +27,10 @@ FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData) {
return result;
}
void spine_report_leaks() {
((DebugExtension*)spine::SpineExtension::getInstance())->reportLeaks();
}
FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) {
if (!atlas) return;
if (atlas->atlas) delete (Atlas*)atlas->atlas;
@ -33,6 +38,7 @@ FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) {
for (int i = 0; i < atlas->numImagePaths; i++) {
free(atlas->imagePaths[i]);
}
SpineExtension::free(atlas->imagePaths, __FILE__, __LINE__);
SpineExtension::free(atlas, __FILE__, __LINE__);
}
@ -74,27 +80,6 @@ FFI_PLUGIN_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data *skeleton
SpineExtension::free(skeletonData, __FILE__, __LINE__);
}
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData) {
spine_skeleton_drawable *drawable = SpineExtension::calloc<spine_skeleton_drawable>(1, __FILE__, __LINE__);
drawable->skeleton = new Skeleton((SkeletonData*)skeletonData->skeletonData);
drawable->animationState = new AnimationState(new AnimationStateData((SkeletonData*)skeletonData->skeletonData));
drawable->clipping = new SkeletonClipping();
return drawable;
}
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_update(spine_skeleton_drawable *drawable, float deltaTime) {
if (!drawable) return;
if (!drawable->skeleton) return;
if (!drawable->animationState) return;
if (!drawable->clipping) return;
Skeleton *skeleton = (Skeleton*)drawable->skeleton;
AnimationState *animationState = (AnimationState*)drawable->animationState;
animationState->update(deltaTime);
animationState->apply(*skeleton);
skeleton->updateWorldTransform();
}
spine_render_command *spine_render_command_create(int32_t numVertices, int32_t numIndices, spine_blend_mode blendMode, int pageIndex) {
spine_render_command *cmd = SpineExtension::alloc<spine_render_command>(1, __FILE__, __LINE__);
cmd->positions = SpineExtension::alloc<float>(numVertices << 1, __FILE__, __LINE__);
@ -118,6 +103,44 @@ void spine_render_command_dispose(spine_render_command *cmd) {
SpineExtension::free(cmd, __FILE__, __LINE__);
}
FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData) {
spine_skeleton_drawable *drawable = SpineExtension::calloc<spine_skeleton_drawable>(1, __FILE__, __LINE__);
drawable->skeleton = new Skeleton((SkeletonData*)skeletonData->skeletonData);
drawable->animationState = new AnimationState(new AnimationStateData((SkeletonData*)skeletonData->skeletonData));
drawable->clipping = new SkeletonClipping();
return drawable;
}
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *drawable) {
if (!drawable) return;
if (drawable->skeleton) delete (Skeleton*)drawable->skeleton;
if (drawable->animationState) {
AnimationState *state = (AnimationState*)drawable->animationState;
delete state->getData();
delete (AnimationState*)state;
}
if (drawable->clipping) delete (SkeletonClipping*)drawable->clipping;
while (drawable->renderCommand) {
spine_render_command *cmd = drawable->renderCommand;
drawable->renderCommand = cmd->next;
spine_render_command_dispose(cmd);
}
SpineExtension::free(drawable, __FILE__, __LINE__);
}
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_update(spine_skeleton_drawable *drawable, float deltaTime) {
if (!drawable) return;
if (!drawable->skeleton) return;
if (!drawable->animationState) return;
if (!drawable->clipping) return;
Skeleton *skeleton = (Skeleton*)drawable->skeleton;
AnimationState *animationState = (AnimationState*)drawable->animationState;
animationState->update(deltaTime);
animationState->apply(*skeleton);
skeleton->updateWorldTransform();
}
FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_skeleton_drawable *drawable) {
if (!drawable) return nullptr;
if (!drawable->skeleton) return nullptr;
@ -238,19 +261,6 @@ FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_ske
return drawable->renderCommand;
}
FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *drawable) {
if (!drawable) return;
if (drawable->skeleton) delete (Skeleton*)drawable->skeleton;
if (drawable->animationState) delete (AnimationState*)drawable->animationState;
if (drawable->clipping) delete (SkeletonClipping*)drawable->clipping;
while (drawable->renderCommand) {
spine_render_command *cmd = drawable->renderCommand;
drawable->renderCommand = cmd->next;
spine_render_command_dispose(cmd);
}
SpineExtension::free(drawable, __FILE__, __LINE__);
}
spine::SpineExtension *spine::getDefaultExtension() {
return new spine::DefaultSpineExtension();
return new spine::DebugExtension(new spine::DefaultSpineExtension());
}

View File

@ -25,6 +25,7 @@
FFI_PLUGIN_EXPORT int32_t spine_major_version();
FFI_PLUGIN_EXPORT int32_t spine_minor_version();
FFI_PLUGIN_EXPORT void spine_report_leaks();
typedef struct spine_atlas {
void *atlas;