com.wallstop-studios.unity-helpers 2.0.0-rc42 → 2.0.0-rc44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/AnimationCopier.cs +1 -1
- package/Editor/AnimationCreator.cs +1 -1
- package/Editor/{EnsureTextureSizeWizard.cs → FitTextureSizeWizard.cs} +47 -15
- package/Editor/PrefabCheckWizard.cs +30 -25
- package/Editor/SpriteSettingsApplier.cs +130 -26
- package/Editor/TextureSettingsApplier.cs +7 -0
- package/Editor/WShowIfPropertyDrawer.cs +63 -0
- package/Editor/WShowIfPropertyDrawer.cs.meta +3 -0
- package/README.md +16 -0
- package/Runtime/Core/Attributes/WShowIfAttribute.cs +16 -0
- package/Runtime/Core/Attributes/WShowIfAttribute.cs.meta +3 -0
- package/Runtime/Core/Extension/ColorExtensions.cs +378 -87
- package/Runtime/Core/Extension/SerializedPropertyExtensions.cs +157 -0
- package/Runtime/Core/Extension/SerializedPropertyExtensions.cs.meta +3 -0
- package/Runtime/Core/Helper/FormattingHelpers.cs +32 -0
- package/Runtime/Core/Helper/FormattingHelpers.cs.meta +3 -0
- package/Runtime/Core/Helper/Objects.cs +4 -2
- package/Runtime/Core/Helper/Partials/TransformHelpers.cs +0 -1
- package/Runtime/Core/Random/AbstractRandom.cs +6 -5
- package/Runtime/Core/Random/DotNetRandom.cs +2 -0
- package/Runtime/Core/Random/IRandom.cs +1 -0
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +49 -0
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs.meta +3 -0
- package/Runtime/Core/Random/PcgRandom.cs +1 -1
- package/Runtime/Core/Random/RomuDuo.cs +1 -1
- package/Runtime/Core/Random/SplitMix64.cs +1 -1
- package/Runtime/Core/Random/SystemRandom.cs +1 -1
- package/Runtime/Core/Random/WyRandom.cs +1 -1
- package/Runtime/Core/Random/XorShiftRandom.cs +13 -8
- package/Runtime/Core/Random/XorShiroRandom.cs +2 -0
- package/Runtime/Core/Threading/SingleThreadedThreadPool.cs +44 -35
- package/Runtime/UI/LayeredImage.cs +2 -2
- package/Runtime/Utils/SpriteRendererMetadata.cs +104 -42
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +21 -3
- package/Tests/Runtime/Random/LinearCongruentialGeneratorTests.cs +12 -0
- package/Tests/Runtime/Random/LinearCongruentialGeneratorTests.cs.meta +3 -0
- package/package.json +1 -1
- /package/Editor/{EnsureTextureSizeWizard.cs.meta → FitTextureSizeWizard.cs.meta} +0 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
namespace UnityHelpers.Core.Extension
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using UnityEditor;
|
|
5
|
+
using System;
|
|
6
|
+
using System.Reflection;
|
|
7
|
+
|
|
8
|
+
public static class SerializedPropertyExtensions
|
|
9
|
+
{
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Gets the instance object that contains the given SerializedProperty.
|
|
12
|
+
/// </summary>
|
|
13
|
+
/// <param name="property">The SerializedProperty.</param>
|
|
14
|
+
/// <param name="fieldInfo">Outputs the FieldInfo of the referenced field.</param>
|
|
15
|
+
/// <returns>The instance object that owns the field.</returns>
|
|
16
|
+
public static object GetEnclosingObject(
|
|
17
|
+
this SerializedProperty property,
|
|
18
|
+
out FieldInfo fieldInfo
|
|
19
|
+
)
|
|
20
|
+
{
|
|
21
|
+
fieldInfo = null;
|
|
22
|
+
object obj = property.serializedObject.targetObject;
|
|
23
|
+
if (obj == null)
|
|
24
|
+
{
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
Type type = obj.GetType();
|
|
28
|
+
string[] pathParts = property.propertyPath.Split('.');
|
|
29
|
+
|
|
30
|
+
// Traverse the path but stop at the second-to-last field
|
|
31
|
+
for (int i = 0; i < pathParts.Length - 1; ++i)
|
|
32
|
+
{
|
|
33
|
+
string fieldName = pathParts[i];
|
|
34
|
+
|
|
35
|
+
if (string.Equals(fieldName, "Array", StringComparison.Ordinal))
|
|
36
|
+
{
|
|
37
|
+
// Move to "data[i]", no need to length-check, we're guarded above
|
|
38
|
+
|
|
39
|
+
++i;
|
|
40
|
+
if (
|
|
41
|
+
!int.TryParse(
|
|
42
|
+
pathParts[i]
|
|
43
|
+
.Replace("data[", string.Empty, StringComparison.Ordinal)
|
|
44
|
+
.Replace("]", string.Empty, StringComparison.Ordinal),
|
|
45
|
+
out int index
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
{
|
|
49
|
+
// Unexpected, die
|
|
50
|
+
fieldInfo = null;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
obj = GetElementAtIndex(obj, index);
|
|
54
|
+
type = obj?.GetType();
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fieldInfo = type?.GetField(
|
|
59
|
+
fieldName,
|
|
60
|
+
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance
|
|
61
|
+
);
|
|
62
|
+
if (fieldInfo == null)
|
|
63
|
+
{
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Move deeper but stop before the last property in the path
|
|
68
|
+
if (i < pathParts.Length - 2)
|
|
69
|
+
{
|
|
70
|
+
obj = fieldInfo.GetValue(obj);
|
|
71
|
+
type = fieldInfo.FieldType;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return obj;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// <summary>
|
|
79
|
+
/// Gets the FieldInfo and the instance object that owns the field for a given SerializedProperty.
|
|
80
|
+
/// </summary>
|
|
81
|
+
/// <param name="property">The SerializedProperty to reflect upon.</param>
|
|
82
|
+
/// <param name="fieldInfo">Outputs the FieldInfo of the referenced field.</param>
|
|
83
|
+
/// <returns>The instance object that owns the field.</returns>
|
|
84
|
+
public static object GetTargetObjectWithField(
|
|
85
|
+
this SerializedProperty property,
|
|
86
|
+
out FieldInfo fieldInfo
|
|
87
|
+
)
|
|
88
|
+
{
|
|
89
|
+
fieldInfo = null;
|
|
90
|
+
object obj = property.serializedObject.targetObject;
|
|
91
|
+
if (obj == null)
|
|
92
|
+
{
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
Type type = obj.GetType();
|
|
97
|
+
string[] pathParts = property.propertyPath.Split('.');
|
|
98
|
+
|
|
99
|
+
for (int i = 0; i < pathParts.Length; ++i)
|
|
100
|
+
{
|
|
101
|
+
string fieldName = pathParts[i];
|
|
102
|
+
|
|
103
|
+
if (string.Equals(fieldName, "Array", StringComparison.Ordinal))
|
|
104
|
+
{
|
|
105
|
+
// Move to "data[i]"
|
|
106
|
+
++i;
|
|
107
|
+
if (pathParts.Length <= i)
|
|
108
|
+
{
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
!int.TryParse(
|
|
114
|
+
pathParts[i]
|
|
115
|
+
.Replace("data[", string.Empty, StringComparison.Ordinal)
|
|
116
|
+
.Replace("]", string.Empty, StringComparison.Ordinal),
|
|
117
|
+
out int index
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
{
|
|
121
|
+
// Unexpected, die
|
|
122
|
+
fieldInfo = null;
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
obj = GetElementAtIndex(obj, index);
|
|
126
|
+
type = obj?.GetType();
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fieldInfo = type?.GetField(
|
|
131
|
+
fieldName,
|
|
132
|
+
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance
|
|
133
|
+
);
|
|
134
|
+
if (fieldInfo == null)
|
|
135
|
+
{
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Move deeper into the object tree
|
|
140
|
+
obj = fieldInfo.GetValue(obj);
|
|
141
|
+
type = fieldInfo.FieldType;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return obj;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private static object GetElementAtIndex(object obj, int index)
|
|
148
|
+
{
|
|
149
|
+
if (obj is System.Collections.IList list && index >= 0 && index < list.Count)
|
|
150
|
+
{
|
|
151
|
+
return list[index];
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
#endif
|
|
157
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
namespace UnityHelpers.Core.Helper
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
|
|
5
|
+
public static class FormattingHelpers
|
|
6
|
+
{
|
|
7
|
+
public static string FormatBytes(long bytes)
|
|
8
|
+
{
|
|
9
|
+
string[] sizes = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
|
|
10
|
+
double len = bytes;
|
|
11
|
+
int order = 0;
|
|
12
|
+
|
|
13
|
+
bytes = Math.Max(0, bytes);
|
|
14
|
+
|
|
15
|
+
const int byteInChunk = 1024;
|
|
16
|
+
while (byteInChunk <= len)
|
|
17
|
+
{
|
|
18
|
+
len /= byteInChunk;
|
|
19
|
+
if (order < sizes.Length - 1)
|
|
20
|
+
{
|
|
21
|
+
++order;
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
{
|
|
25
|
+
throw new ArgumentException($"Too many bytes! Cannot parse {bytes}");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return $"{len:0.##} {sizes[order]}";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
21
|
-
public static bool Null(
|
|
21
|
+
public static bool Null<T>(T instance)
|
|
22
|
+
where T : UnityEngine.Object
|
|
22
23
|
{
|
|
23
24
|
return instance == null;
|
|
24
25
|
}
|
|
@@ -29,7 +30,8 @@
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
32
|
-
public static bool NotNull(
|
|
33
|
+
public static bool NotNull<T>(T instance)
|
|
34
|
+
where T : UnityEngine.Object
|
|
33
35
|
{
|
|
34
36
|
return instance != null;
|
|
35
37
|
}
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
public abstract RandomState InternalState { get; }
|
|
23
23
|
|
|
24
|
+
private readonly byte[] _guidBytes = new byte[16];
|
|
25
|
+
|
|
24
26
|
public virtual int Next()
|
|
25
27
|
{
|
|
26
28
|
// Mask out the MSB to ensure the value is within [0, int.MaxValue]
|
|
@@ -410,24 +412,23 @@
|
|
|
410
412
|
where T : struct, Enum
|
|
411
413
|
{
|
|
412
414
|
Type enumType = typeof(T);
|
|
413
|
-
T[] enumValues = (T[])EnumTypeCache.GetOrAdd(enumType, Enum.GetValues);
|
|
415
|
+
T[] enumValues = (T[])EnumTypeCache.GetOrAdd(enumType, type => Enum.GetValues(type));
|
|
414
416
|
|
|
415
417
|
return RandomOf(enumValues);
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
public Guid NextGuid()
|
|
419
421
|
{
|
|
420
|
-
return new Guid(GenerateGuidBytes());
|
|
422
|
+
return new Guid(GenerateGuidBytes(_guidBytes));
|
|
421
423
|
}
|
|
422
424
|
|
|
423
425
|
public KGuid NextKGuid()
|
|
424
426
|
{
|
|
425
|
-
return new KGuid(GenerateGuidBytes());
|
|
427
|
+
return new KGuid(GenerateGuidBytes(_guidBytes));
|
|
426
428
|
}
|
|
427
429
|
|
|
428
|
-
private byte[] GenerateGuidBytes()
|
|
430
|
+
private byte[] GenerateGuidBytes(byte[] guidBytes)
|
|
429
431
|
{
|
|
430
|
-
byte[] guidBytes = new byte[16];
|
|
431
432
|
NextBytes(guidBytes);
|
|
432
433
|
SetUuidV4Bits(guidBytes);
|
|
433
434
|
return guidBytes;
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
[DataContract]
|
|
9
9
|
public sealed class DotNetRandom : AbstractRandom
|
|
10
10
|
{
|
|
11
|
+
public static DotNetRandom Instance => ThreadLocalRandom<DotNetRandom>.Instance;
|
|
12
|
+
|
|
11
13
|
public override RandomState InternalState =>
|
|
12
14
|
new RandomState(unchecked((ulong)_seed), state2: _numberGenerated);
|
|
13
15
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
namespace UnityHelpers.Core.Random
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Text.Json.Serialization;
|
|
5
|
+
|
|
6
|
+
public sealed class LinearCongruentialGenerator : AbstractRandom
|
|
7
|
+
{
|
|
8
|
+
public static LinearCongruentialGenerator Instance =>
|
|
9
|
+
ThreadLocalRandom<LinearCongruentialGenerator>.Instance;
|
|
10
|
+
|
|
11
|
+
public override RandomState InternalState => new(_state, 0, _cachedGaussian);
|
|
12
|
+
|
|
13
|
+
private uint _state;
|
|
14
|
+
|
|
15
|
+
public LinearCongruentialGenerator()
|
|
16
|
+
: this(Guid.NewGuid()) { }
|
|
17
|
+
|
|
18
|
+
public LinearCongruentialGenerator(int seed)
|
|
19
|
+
{
|
|
20
|
+
_state = unchecked((uint)seed);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public LinearCongruentialGenerator(Guid seed)
|
|
24
|
+
{
|
|
25
|
+
_state = unchecked((uint)seed.GetHashCode());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[JsonConstructor]
|
|
29
|
+
public LinearCongruentialGenerator(RandomState internalState)
|
|
30
|
+
{
|
|
31
|
+
_state = unchecked((uint)internalState.State1);
|
|
32
|
+
_cachedGaussian = internalState.Gaussian;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public override uint NextUint()
|
|
36
|
+
{
|
|
37
|
+
unchecked
|
|
38
|
+
{
|
|
39
|
+
_state = _state * 1664525U + 1013904223U;
|
|
40
|
+
}
|
|
41
|
+
return _state;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public override IRandom Copy()
|
|
45
|
+
{
|
|
46
|
+
return new LinearCongruentialGenerator(InternalState);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
IComparable,
|
|
16
16
|
IComparable<PcgRandom>
|
|
17
17
|
{
|
|
18
|
-
public static
|
|
18
|
+
public static PcgRandom Instance => ThreadLocalRandom<PcgRandom>.Instance;
|
|
19
19
|
|
|
20
20
|
public override RandomState InternalState => new(_state, _increment, _cachedGaussian);
|
|
21
21
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
IComparable,
|
|
16
16
|
IComparable<RomuDuo>
|
|
17
17
|
{
|
|
18
|
-
public static
|
|
18
|
+
public static RomuDuo Instance => ThreadLocalRandom<RomuDuo>.Instance;
|
|
19
19
|
public override RandomState InternalState => new(_x, _y, _cachedGaussian);
|
|
20
20
|
|
|
21
21
|
internal ulong _x;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
IComparable,
|
|
13
13
|
IComparable<SplitMix64>
|
|
14
14
|
{
|
|
15
|
-
public static
|
|
15
|
+
public static SplitMix64 Instance => ThreadLocalRandom<SplitMix64>.Instance;
|
|
16
16
|
|
|
17
17
|
public override RandomState InternalState => new(_state, 0, _cachedGaussian);
|
|
18
18
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
private const int SeedArraySize = 56;
|
|
17
17
|
private const int LastSeedIndex = SeedArraySize - 1;
|
|
18
18
|
|
|
19
|
-
public static
|
|
19
|
+
public static SystemRandom Instance => ThreadLocalRandom<SystemRandom>.Instance;
|
|
20
20
|
|
|
21
21
|
public override RandomState InternalState =>
|
|
22
22
|
new(
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
private const ulong Prime0 = 0xa0761d6478bd642f;
|
|
15
15
|
private const ulong Prime1 = 0xe7037ed1a0b428db;
|
|
16
16
|
|
|
17
|
-
public static
|
|
17
|
+
public static WyRandom Instance => ThreadLocalRandom<WyRandom>.Instance;
|
|
18
18
|
|
|
19
19
|
public override RandomState InternalState => new RandomState(_state);
|
|
20
20
|
|
|
@@ -8,18 +8,25 @@
|
|
|
8
8
|
[DataContract]
|
|
9
9
|
public sealed class XorShiftRandom : AbstractRandom
|
|
10
10
|
{
|
|
11
|
-
public static
|
|
11
|
+
public static XorShiftRandom Instance => ThreadLocalRandom<XorShiftRandom>.Instance;
|
|
12
12
|
|
|
13
13
|
public override RandomState InternalState => new(_state, 0, _cachedGaussian);
|
|
14
14
|
|
|
15
15
|
private uint _state;
|
|
16
16
|
|
|
17
17
|
public XorShiftRandom()
|
|
18
|
-
: this(Guid.NewGuid()
|
|
18
|
+
: this(Guid.NewGuid()) { }
|
|
19
19
|
|
|
20
20
|
public XorShiftRandom(int state)
|
|
21
21
|
{
|
|
22
22
|
_state = unchecked((uint)state);
|
|
23
|
+
_state = _state != 0 ? _state : 2463534242U;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public XorShiftRandom(Guid seed)
|
|
27
|
+
{
|
|
28
|
+
_state = unchecked((uint)seed.GetHashCode());
|
|
29
|
+
_state = _state != 0 ? _state : 2463534242U;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
[JsonConstructor]
|
|
@@ -31,12 +38,10 @@
|
|
|
31
38
|
|
|
32
39
|
public override uint NextUint()
|
|
33
40
|
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
_state = state;
|
|
39
|
-
return state;
|
|
41
|
+
_state ^= _state << 13;
|
|
42
|
+
_state ^= _state >> 17;
|
|
43
|
+
_state ^= _state << 5;
|
|
44
|
+
return _state;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
public override IRandom Copy()
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
public sealed class SingleThreadedThreadPool : IDisposable
|
|
8
8
|
{
|
|
9
9
|
public ConcurrentQueue<Exception> Exceptions => _exceptions;
|
|
10
|
+
public int Count => _work.Count + Interlocked.CompareExchange(ref _working, 0, 0);
|
|
10
11
|
|
|
11
12
|
private int _active;
|
|
12
13
|
private int _working;
|
|
@@ -16,14 +17,14 @@
|
|
|
16
17
|
private bool _disposed;
|
|
17
18
|
private readonly ConcurrentQueue<Exception> _exceptions;
|
|
18
19
|
|
|
19
|
-
public SingleThreadedThreadPool()
|
|
20
|
+
public SingleThreadedThreadPool(bool runInBackground = false)
|
|
20
21
|
{
|
|
21
22
|
_active = 1;
|
|
22
23
|
_working = 1;
|
|
23
24
|
_work = new ConcurrentQueue<Action>();
|
|
24
25
|
_exceptions = new ConcurrentQueue<Exception>();
|
|
25
26
|
_waitHandle = new AutoResetEvent(false);
|
|
26
|
-
_worker = new Thread(DoWork);
|
|
27
|
+
_worker = new Thread(DoWork) { IsBackground = runInBackground };
|
|
27
28
|
_worker.Start();
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -32,40 +33,12 @@
|
|
|
32
33
|
Dispose(false);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
private void DoWork()
|
|
36
|
-
{
|
|
37
|
-
while (Interlocked.CompareExchange(ref _active, 0, 0) != 0)
|
|
38
|
-
{
|
|
39
|
-
_ = Interlocked.Exchange(ref _working, 0);
|
|
40
|
-
if (_work.TryDequeue(out Action workItem))
|
|
41
|
-
{
|
|
42
|
-
_ = Interlocked.Exchange(ref _working, 1);
|
|
43
|
-
try
|
|
44
|
-
{
|
|
45
|
-
workItem();
|
|
46
|
-
}
|
|
47
|
-
catch (Exception e)
|
|
48
|
-
{
|
|
49
|
-
_exceptions.Enqueue(e);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else
|
|
53
|
-
{
|
|
54
|
-
_ = _waitHandle.WaitOne(TimeSpan.FromSeconds(1));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
_ = Interlocked.Exchange(ref _working, 0);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
36
|
public void Enqueue(Action work)
|
|
62
37
|
{
|
|
63
38
|
_work.Enqueue(work);
|
|
64
39
|
_ = _waitHandle.Set();
|
|
65
40
|
}
|
|
66
41
|
|
|
67
|
-
public int Count => _work.Count + Interlocked.CompareExchange(ref _working, 0, 0);
|
|
68
|
-
|
|
69
42
|
public void Dispose()
|
|
70
43
|
{
|
|
71
44
|
Dispose(true);
|
|
@@ -79,17 +52,16 @@
|
|
|
79
52
|
return;
|
|
80
53
|
}
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
do
|
|
55
|
+
while (Interlocked.CompareExchange(ref _active, 0, _active) != 0)
|
|
84
56
|
{
|
|
85
|
-
|
|
86
|
-
}
|
|
57
|
+
// Spin
|
|
58
|
+
}
|
|
87
59
|
|
|
88
60
|
if (disposing)
|
|
89
61
|
{
|
|
90
62
|
try
|
|
91
63
|
{
|
|
92
|
-
_worker?.Join();
|
|
64
|
+
_worker?.Join(TimeSpan.FromSeconds(30));
|
|
93
65
|
_waitHandle?.Dispose();
|
|
94
66
|
}
|
|
95
67
|
catch
|
|
@@ -103,5 +75,42 @@
|
|
|
103
75
|
|
|
104
76
|
_disposed = true;
|
|
105
77
|
}
|
|
78
|
+
|
|
79
|
+
private void DoWork()
|
|
80
|
+
{
|
|
81
|
+
while (Interlocked.CompareExchange(ref _active, 0, 0) != 0)
|
|
82
|
+
{
|
|
83
|
+
try
|
|
84
|
+
{
|
|
85
|
+
if (_work.TryDequeue(out Action workItem))
|
|
86
|
+
{
|
|
87
|
+
_ = Interlocked.Exchange(ref _working, 1);
|
|
88
|
+
try
|
|
89
|
+
{
|
|
90
|
+
workItem();
|
|
91
|
+
}
|
|
92
|
+
catch (Exception e)
|
|
93
|
+
{
|
|
94
|
+
_exceptions.Enqueue(e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
{
|
|
99
|
+
try
|
|
100
|
+
{
|
|
101
|
+
_ = _waitHandle?.WaitOne(TimeSpan.FromSeconds(1));
|
|
102
|
+
}
|
|
103
|
+
catch (ObjectDisposedException)
|
|
104
|
+
{
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
finally
|
|
110
|
+
{
|
|
111
|
+
_ = Interlocked.Exchange(ref _working, 0);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
106
115
|
}
|
|
107
116
|
}
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
int maxY = int.MinValue;
|
|
177
177
|
foreach (AnimatedSpriteLayer layer in _layers)
|
|
178
178
|
{
|
|
179
|
-
if (
|
|
179
|
+
if (layer.frames.Length <= 0)
|
|
180
180
|
{
|
|
181
181
|
continue;
|
|
182
182
|
}
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
|
|
211
211
|
foreach (AnimatedSpriteLayer layer in _layers)
|
|
212
212
|
{
|
|
213
|
-
if (
|
|
213
|
+
if (layer.frames.Length <= 0)
|
|
214
214
|
{
|
|
215
215
|
continue;
|
|
216
216
|
}
|