|
4 | 4 |
|
5 | 5 | using System; |
6 | 6 | using System.Collections.Generic; |
| 7 | +using System.Runtime.CompilerServices; |
7 | 8 | using System.Text; |
8 | 9 |
|
9 | 10 | namespace Source.MaterialSystem; |
10 | 11 |
|
11 | 12 |
|
12 | | -public class MatCallQueue : ICallQueue |
| 13 | +public sealed class MatCallQueue : ICallQueue |
13 | 14 | { |
| 15 | + abstract class CallBuffer |
| 16 | + { |
| 17 | + public bool Used; |
| 18 | + public abstract void Execute(int index); |
| 19 | + public abstract void Clear(); |
| 20 | + } |
| 21 | + |
| 22 | + sealed class ActionBuffer : CallBuffer |
| 23 | + { |
| 24 | + Action[] Funcs = new Action[64]; |
| 25 | + int Count; |
| 26 | + |
| 27 | + public int Add(Action func) { |
| 28 | + if (Count == Funcs.Length) |
| 29 | + Array.Resize(ref Funcs, Count * 2); |
| 30 | + Funcs[Count] = func; |
| 31 | + return Count++; |
| 32 | + } |
| 33 | + |
| 34 | + public override void Execute(int index) => Funcs[index](); |
| 35 | + |
| 36 | + public override void Clear() { |
| 37 | + Array.Clear(Funcs, 0, Count); |
| 38 | + Count = 0; |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + sealed class CallBuffer<T> : CallBuffer |
| 43 | + { |
| 44 | + Action<T>[] Funcs = new Action<T>[16]; |
| 45 | + T[] Args = new T[16]; |
| 46 | + int Count; |
| 47 | + |
| 48 | + public int Add(Action<T> func, in T args) { |
| 49 | + if (Count == Args.Length) { |
| 50 | + Array.Resize(ref Funcs, Count * 2); |
| 51 | + Array.Resize(ref Args, Count * 2); |
| 52 | + } |
| 53 | + Funcs[Count] = func; |
| 54 | + Args[Count] = args; |
| 55 | + return Count++; |
| 56 | + } |
| 57 | + |
| 58 | + public override void Execute(int index) => Funcs[index](Args[index]); |
| 59 | + |
| 60 | + public override void Clear() { |
| 61 | + Array.Clear(Funcs, 0, Count); |
| 62 | + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) |
| 63 | + Array.Clear(Args, 0, Count); |
| 64 | + Count = 0; |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + struct Entry |
| 69 | + { |
| 70 | + public CallBuffer Buffer; |
| 71 | + public int Index; |
| 72 | + } |
| 73 | + |
| 74 | + static int NextTypeID = 1; |
| 75 | + static class TypeID<T> |
| 76 | + { |
| 77 | + public static readonly int Value = Interlocked.Increment(ref NextTypeID) - 1; |
| 78 | + } |
| 79 | + |
| 80 | + readonly ActionBuffer Actions = new(); |
| 81 | + CallBuffer?[] Buffers = new CallBuffer?[64]; |
| 82 | + readonly List<CallBuffer> UsedBuffers = []; |
| 83 | + Entry[] Entries = new Entry[4096]; |
| 84 | + int EntryCount; |
| 85 | + |
| 86 | + public int Count() => EntryCount; |
| 87 | + |
| 88 | + void AddEntry(CallBuffer buffer, int index) { |
| 89 | + if (!buffer.Used) { |
| 90 | + buffer.Used = true; |
| 91 | + UsedBuffers.Add(buffer); |
| 92 | + } |
| 93 | + if (EntryCount == Entries.Length) |
| 94 | + Array.Resize(ref Entries, EntryCount * 2); |
| 95 | + ref Entry entry = ref Entries[EntryCount++]; |
| 96 | + entry.Buffer = buffer; |
| 97 | + entry.Index = index; |
| 98 | + } |
| 99 | + |
| 100 | + public void QueueCall(Action func) => AddEntry(Actions, Actions.Add(func)); |
| 101 | + |
| 102 | + public void QueueCall<T>(Action<T> func, in T args) { |
| 103 | + int id = TypeID<T>.Value; |
| 104 | + if (id >= Buffers.Length) |
| 105 | + Array.Resize(ref Buffers, Math.Max(id + 1, Buffers.Length * 2)); |
| 106 | + CallBuffer<T> buffer = (CallBuffer<T>?)Buffers[id] ?? (CallBuffer<T>)(Buffers[id] = new CallBuffer<T>()); |
| 107 | + AddEntry(buffer, buffer.Add(func, in args)); |
| 108 | + } |
| 109 | + |
| 110 | + public void CallQueued() { |
| 111 | + for (int i = 0; i < EntryCount; i++) { |
| 112 | + ref Entry entry = ref Entries[i]; |
| 113 | + entry.Buffer.Execute(entry.Index); |
| 114 | + } |
| 115 | + Flush(); |
| 116 | + } |
| 117 | + |
| 118 | + public void Flush() { |
| 119 | + Array.Clear(Entries, 0, EntryCount); |
| 120 | + EntryCount = 0; |
| 121 | + for (int i = 0; i < UsedBuffers.Count; i++) { |
| 122 | + UsedBuffers[i].Clear(); |
| 123 | + UsedBuffers[i].Used = false; |
| 124 | + } |
| 125 | + UsedBuffers.Clear(); |
| 126 | + } |
| 127 | + |
14 | 128 | public void QueueFunctorInternal(ref Functor functor) { |
15 | 129 | throw new NotImplementedException(); |
16 | 130 | } |
@@ -42,7 +156,7 @@ public interface IMaterialSystemInternal : IMaterialSystem |
42 | 156 | bool CanUseEditorMaterials(); |
43 | 157 | ReadOnlySpan<char> GetForcedTextureLoadPathID(); |
44 | 158 |
|
45 | | - MatCallQueue GetRenderCallQueue(); |
| 159 | + MatCallQueue? GetRenderCallQueue(); |
46 | 160 |
|
47 | 161 | void UnbindMaterial(IMaterial? material); |
48 | 162 | uint GetRenderThreadId(); |
|
0 commit comments