[unity] Added option for unsafe data loading avoiding some allocations on skel.bytes data. Closes #2851.

This commit is contained in:
Harald Csaszar 2025-05-16 15:27:04 +02:00
parent 430b54de5e
commit fb8bc402e7
6 changed files with 85 additions and 21 deletions

View File

@ -175,6 +175,7 @@
2. Add a `RenderExistingMeshGraphic` component.
3. In the `RenderExistingMeshGraphic` component Inspector at `Reference Skeleton Graphic` assign the original `SkeletonGraphic` object.
4. At `Replacement Material` assign e.g. the included _SkeletonGraphicDefaultOutline_ material to replace all materials with this material. Alternatively, if `Multiple CanvasRenderers` is enabled at the reference SkeletonGraphic, you can add entries to the `Replacement Materials` list and at each entry assign the original SkeletonGraphic material (e.g. _SkeletonGraphicDefault_) to be replaced and the respective `Replacement Material` (e.g. _SkeletonGraphicDefaultOutline_).
- Added option for unsafe direct data loading when loading skeleton binary data to avoid some allocations, enabled via build define `SPINE_ALLOW_UNSAFE`. This define can be set via Spine Preferences, setting `Unsafe Build Defines - Direct data access`. The define is disabled by default to maintain existing behaviour. Changed asmdef setting for spine-unity assembly to allow unsafe code, has no effect other than allowing setting the `SPINE_ALLOW_UNSAFE` define.
- **Breaking changes**

View File

@ -83,6 +83,8 @@ namespace Spine.Unity.Editor {
}
public static class SpineBuildEnvUtility {
public const string SPINE_ALLOW_UNSAFE_CODE = "SPINE_ALLOW_UNSAFE";
static bool IsInvalidGroup (BuildTargetGroup group) {
int gi = (int)group;
return
@ -99,15 +101,18 @@ namespace Spine.Unity.Editor {
if (IsInvalidGroup(group))
continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
if (!defines.Contains(define)) {
wasDefineAdded = true;
if (defines.EndsWith(";", System.StringComparison.Ordinal))
defines += define;
else
defines += ";" + define;
try {
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
if (!defines.Contains(define)) {
wasDefineAdded = true;
if (defines.EndsWith(";", System.StringComparison.Ordinal))
defines += define;
else
defines += ";" + define;
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
}
} catch (System.Exception) {
}
}
Debug.LogWarning("Please ignore errors \"PlayerSettings Validation: Requested build target group doesn't exist\" above");
@ -127,15 +132,18 @@ namespace Spine.Unity.Editor {
if (IsInvalidGroup(group))
continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
if (defines.Contains(define)) {
wasDefineRemoved = true;
if (defines.Contains(define + ";"))
defines = defines.Replace(define + ";", "");
else
defines = defines.Replace(define, "");
try {
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
if (defines.Contains(define)) {
wasDefineRemoved = true;
if (defines.Contains(define + ";"))
defines = defines.Replace(define + ";", "");
else
defines = defines.Replace(define, "");
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
}
} catch (System.Exception) {
}
}

View File

@ -39,6 +39,14 @@
#define HAS_ON_POSTPROCESS_PREFAB
#endif
#if UNITY_2021_2_OR_NEWER
#define TEXT_ASSET_HAS_GET_DATA_BYTES
#endif
#if TEXT_ASSET_HAS_GET_DATA_BYTES
#define HAS_ANY_UNSAFE_OPTIONS
#endif
using System.Threading;
using UnityEditor;
using UnityEngine;
@ -356,6 +364,18 @@ namespace Spine.Unity.Editor {
}
#endif
#if HAS_ANY_UNSAFE_OPTIONS
GUILayout.Space(20);
EditorGUILayout.LabelField("Unsafe Build Defines", EditorStyles.boldLabel);
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.PrefixLabel(new GUIContent("Direct data access", "Allow unsafe direct data access. Currently affects reading .skel.bytes files, reading with fewer allocations."));
if (GUILayout.Button("Enable", GUILayout.Width(64)))
SpineBuildEnvUtility.EnableBuildDefine(SpineBuildEnvUtility.SPINE_ALLOW_UNSAFE_CODE);
if (GUILayout.Button("Disable", GUILayout.Width(64)))
SpineBuildEnvUtility.DisableBuildDefine(SpineBuildEnvUtility.SPINE_ALLOW_UNSAFE_CODE);
}
#endif
#if SPINE_TK2D_DEFINE
bool isTK2DDefineSet = true;
#else

View File

@ -1,4 +1,5 @@
{
"name": "spine-unity",
"references": [ "spine-csharp" ]
"references": [ "spine-csharp" ],
"allowUnsafeCode": true
}

View File

@ -27,14 +27,37 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
//#define SPINE_ALLOW_UNSAFE // note: this define can be set via Edit - Preferences - Spine.
#if UNITY_2021_2_OR_NEWER
#define TEXT_ASSET_HAS_GET_DATA_BYTES
#endif
#if SPINE_ALLOW_UNSAFE && TEXT_ASSET_HAS_GET_DATA_BYTES
#define UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
#endif
using System;
using System.Collections.Generic;
using System.IO;
using Unity.Collections;
using UnityEngine;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity {
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
public static class TextAssetExtensions {
public static Stream GetStreamUnsafe (this TextAsset textAsset) {
NativeArray<byte> dataNativeArray = textAsset.GetData<byte>();
return dataNativeArray.GetUnmanagedMemoryStream();
}
public static unsafe UnmanagedMemoryStream GetUnmanagedMemoryStream<T> (this NativeArray<T> nativeArray) where T : struct {
return new UnmanagedMemoryStream((byte*)global::Unity.Collections.LowLevel.Unsafe.
NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(nativeArray), nativeArray.Length);
}
}
#endif
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
public class SkeletonDataAsset : ScriptableObject {
@ -188,9 +211,13 @@ namespace Spine.Unity {
SkeletonData loadedSkeletonData = null;
try {
if (hasBinaryExtension)
if (hasBinaryExtension) {
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.GetStreamUnsafe(), attachmentLoader, skeletonDataScale);
#else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
#endif
} else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
@ -287,6 +314,13 @@ namespace Spine.Unity {
}
}
internal static SkeletonData ReadSkeletonData (Stream assetStream, AttachmentLoader attachmentLoader, float scale) {
SkeletonBinary binary = new SkeletonBinary(attachmentLoader) {
Scale = scale
};
return binary.ReadSkeletonData(assetStream);
}
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
StringReader input = new StringReader(text);
SkeletonJson json = new SkeletonJson(attachmentLoader) {

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-unity",
"displayName": "spine-unity Runtime",
"description": "This plugin provides the spine-unity runtime core.",
"version": "4.2.104",
"version": "4.2.105",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",