├── LICENSE
├── NoAllocHelpers.cs
├── RefreshAndPlayButton.cs
└── ViewAsNativeArrayExtensions.cs
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Per-Morten Straume
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NoAllocHelpers.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright(c) 2019 Timothy Raines
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | using System;
26 | using System.Collections.Generic;
27 | using System.Reflection;
28 |
29 | using UnityEngine;
30 |
31 | ///
32 | /// Provides access to the internal UnityEngine.NoAllocHelpers methods.
33 | ///
34 | public static class NoAllocHelpers
35 | {
36 | private static readonly Dictionary ExtractArrayFromListTDelegates = new Dictionary();
37 | private static readonly Dictionary ResizeListDelegates = new Dictionary();
38 |
39 | ///
40 | /// Extract the internal array from a list.
41 | ///
42 | /// .
43 | /// The to extract from.
44 | /// The internal array of the list.
45 | public static T[] ExtractArrayFromListT(List list)
46 | {
47 | if (!ExtractArrayFromListTDelegates.TryGetValue(typeof(T), out var obj))
48 | {
49 | var ass = Assembly.GetAssembly(typeof(Mesh)); // any class in UnityEngine
50 | var type = ass.GetType("UnityEngine.NoAllocHelpers");
51 | var methodInfo = type.GetMethod("ExtractArrayFromListT", BindingFlags.Static | BindingFlags.Public)
52 | .MakeGenericMethod(typeof(T));
53 |
54 | obj = ExtractArrayFromListTDelegates[typeof(T)] = Delegate.CreateDelegate(typeof(Func, T[]>), methodInfo);
55 | }
56 |
57 | var func = (Func, T[]>)obj;
58 | return func.Invoke(list);
59 | }
60 |
61 | ///
62 | /// Resize a list.
63 | ///
64 | /// .
65 | /// The to resize.
66 | /// The new length of the .
67 | public static void ResizeList(List list, int size)
68 | {
69 | if (!ResizeListDelegates.TryGetValue(typeof(T), out var obj))
70 | {
71 | var ass = Assembly.GetAssembly(typeof(Mesh)); // any class in UnityEngine
72 | var type = ass.GetType("UnityEngine.NoAllocHelpers");
73 | var methodInfo = type.GetMethod("ResizeList", BindingFlags.Static | BindingFlags.Public)
74 | .MakeGenericMethod(typeof(T));
75 | obj = ResizeListDelegates[typeof(T)] =
76 | Delegate.CreateDelegate(typeof(Action, int>), methodInfo);
77 | }
78 |
79 | var action = (Action, int>)obj;
80 | action.Invoke(list, size);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/RefreshAndPlayButton.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using UnityEngine;
3 |
4 | public class RefreshAndPlayButton
5 | : EditorWindow
6 | {
7 | [MenuItem("Open Refresh and Play Button Window", priority = 1)]
8 | public static void Init()
9 | {
10 | var window = EditorWindow.GetWindow();
11 | window.titleContent.text = "Refresh and Play";
12 | }
13 |
14 | private void OnGUI()
15 | {
16 | using (new EditorGUILayout.HorizontalScope())
17 | {
18 | if (GUILayout.Button("Refresh and Play"))
19 | {
20 | AssetDatabase.Refresh(ImportAssetOptions.Default);
21 | EditorApplication.EnterPlaymode();
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ViewAsNativeArrayExtensions.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// MIT License
3 | ///
4 | /// Copyright (c) 2024 Per-Morten Straume
5 | ///
6 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
7 | /// of this software and associated documentation files (the "Software"), to deal
8 | /// in the Software without restriction, including without limitation the rights
9 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | /// copies of the Software, and to permit persons to whom the Software is
11 | /// furnished to do so, subject to the following conditions:
12 | ///
13 | /// The above copyright notice and this permission notice shall be included in all
14 | /// copies or substantial portions of the Software.
15 | ///
16 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | /// SOFTWARE.
23 | ///
24 |
25 | using System;
26 | using System.Collections.Generic;
27 | using Unity.Burst;
28 | using Unity.Collections;
29 | using Unity.Collections.LowLevel.Unsafe;
30 | using Unity.Jobs;
31 |
32 | public static class ViewAsNativeArrayExtensions
33 | {
34 | ///
35 | /// View as a without having to copy it or doing all the boilerplate for getting a pointer to the array.
36 | /// Useful for allowing a job to work on an array.
37 | ///
38 | ///
39 | /// You do not need to dispose the , but you need to dispose the you get back, Unity's Memory Leak Detection will tell you if you forget.
40 | /// Do not use the after calling on the returned from this function,
41 | /// as you can risk the garbage collector removing the data from down under you, Unity's Collections Safety Checks will tell you if you do this.
42 | /// There is no race detection for accessing multiple different views of the same array in different jobs concurrently.
43 | ///
44 | ///
45 | /// Usage:
46 | ///
47 | /// int[] array;
48 | /// using (array.ViewAsNativeArray(out var nativeArray))
49 | /// {
50 | /// // work on nativeArray
51 | /// }
52 | ///
53 | ///
54 | public unsafe static NativeArrayViewHandle ViewAsNativeArray(this T[] array, out NativeArray nativeArray) where T : struct
55 | {
56 | var ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out var handle);
57 | nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, array.Length, Allocator.None);
58 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
59 | DisposeSentinel.Create(out var safety, out var sentinel, 0, Allocator.None);
60 | NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, safety);
61 | return new NativeArrayViewHandle(handle, safety, sentinel);
62 | #else
63 | return new NativeArrayViewHandle(handle);
64 | #endif
65 | }
66 |
67 | ///
68 | /// View as a without having to copy it or doing all the boilerplate for getting the pointer out of a list.
69 | /// Useful for allowing a job to work on a list.
70 | ///
71 | ///
72 | /// Put this thing in a disposable scope unless you can guarantee that the list will never change size or reallocate (in that case consider using a instead),
73 | /// as Unity will not tell you if you're out of bounds, accessing invalid data, or accessing stale data because you have a stale/invalid view of the list.
74 | /// The following changes to the list will turn a view invalid/stale:
75 | ///
76 | /// - The contents of the array will be stale (not reflect any changes to the values in the list) in case of a reallocation (changes to, or adding more items than, or using )
77 | /// - The length of the array will be wrong if you add/remove elements from the list
78 | ///
79 | ///
80 | ///
81 | ///
82 | /// The itself does not need to be disposed, but you need to dispose the you get back, Unity's Memory Leak Detection will tell you if you forget.
83 | /// Do not use the array after calling on the returned from this function,
84 | /// as you can risk the garbage collector removing the data from down under you, Unity's Collections Safety Checks will tell you if you do this.
85 | /// There is no race detection for accessing multiple different views of the same list in different jobs concurrently, or modifying the list while a job is working on a view.
86 | ///
87 | ///
88 | /// Usage:
89 | ///
90 | /// List<int> list;
91 | /// using (list.ViewAsNativeArray(out var nativeArray))
92 | /// {
93 | /// // work on nativeArray
94 | /// }
95 | ///
96 | ///
97 | public unsafe static NativeArrayViewHandle ViewAsNativeArray(this List list, out NativeArray nativeArray) where T : struct
98 | {
99 | var lArray = NoAllocHelpers.ExtractArrayFromListT(list);
100 | var ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(lArray, out var handle);
101 | nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, list.Count, Allocator.None);
102 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
103 | DisposeSentinel.Create(out var safety, out var sentinel, 0, Allocator.None);
104 | NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, safety);
105 | return new NativeArrayViewHandle(handle, safety, sentinel);
106 | #else
107 | return new NativeArrayViewHandle(handle);
108 | #endif
109 | }
110 |
111 | public struct NativeArrayViewHandle
112 | : IDisposable
113 | {
114 | private ulong m_GCHandle;
115 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
116 | private AtomicSafetyHandle m_Safety;
117 | private DisposeSentinel m_DisposeSentinel;
118 | #endif
119 |
120 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
121 | public NativeArrayViewHandle(ulong gcHandle, AtomicSafetyHandle safety, DisposeSentinel disposeSentinel)
122 | {
123 | m_Safety = safety;
124 | m_DisposeSentinel = disposeSentinel;
125 | m_GCHandle = gcHandle;
126 | }
127 | #else
128 | public NativeArrayViewHandle(ulong gcHandle)
129 | {
130 | m_GCHandle = gcHandle;
131 | }
132 | #endif
133 |
134 | public void Dispose()
135 | {
136 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
137 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
138 | #endif
139 | UnsafeUtility.ReleaseGCObject(m_GCHandle);
140 | }
141 |
142 | public JobHandle Dispose(JobHandle dependsOn)
143 | {
144 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
145 | DisposeSentinel.Clear(ref m_DisposeSentinel);
146 |
147 | var jobHandle = new NativeArrayViewHandleDisposeJob
148 | {
149 | Data = new NativeArrayViewHandleDispose(m_GCHandle, m_Safety),
150 | }
151 | .Schedule(dependsOn);
152 |
153 | AtomicSafetyHandle.Release(m_Safety);
154 | return jobHandle;
155 | #else
156 | return new NativeArrayViewHandleDisposeJob
157 | {
158 | Data = new NativeArrayViewHandleDispose(m_GCHandle),
159 | }
160 | .Schedule(dependsOn);
161 | #endif
162 | }
163 |
164 | [NativeContainer]
165 | private struct NativeArrayViewHandleDispose
166 | {
167 | private ulong m_GCHandle;
168 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
169 | private AtomicSafetyHandle m_Safety;
170 | #endif
171 |
172 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
173 | public NativeArrayViewHandleDispose(ulong gcHandle, AtomicSafetyHandle safety)
174 | {
175 | m_GCHandle = gcHandle;
176 | m_Safety = safety;
177 | }
178 | #else
179 | public NativeArrayViewHandleDispose(ulong gcHandle)
180 | {
181 | m_GCHandle = gcHandle;
182 | }
183 | #endif
184 |
185 | public void Dispose()
186 | {
187 | UnsafeUtility.ReleaseGCObject(m_GCHandle);
188 | }
189 | }
190 |
191 | [BurstCompile]
192 | private struct NativeArrayViewHandleDisposeJob : IJob
193 | {
194 | public NativeArrayViewHandleDispose Data;
195 |
196 | public void Execute()
197 | {
198 | Data.Dispose();
199 | }
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------