├── 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 | --------------------------------------------------------------------------------