diff --git a/Game.Client/C_BaseAnimating.cs b/Game.Client/C_BaseAnimating.cs index 1fcfc6408..fc66f0dda 100644 --- a/Game.Client/C_BaseAnimating.cs +++ b/Game.Client/C_BaseAnimating.cs @@ -283,7 +283,7 @@ public void Dispose() { Vector3 BonePosition; QAngle BoneAngles; Handle AttachedTo = new(); - readonly List CachedBoneData = []; + Matrix3x4[] CachedBoneData = []; public void InvalidateBoneCache() { MostRecentModelBoneCounter = ModelBoneCounter - 1; @@ -533,7 +533,7 @@ public override bool SetupBones(Span boneToWorldOut, int maxBones, in AccumulatedBoneMask = 0; } - int nBoneCount = CachedBoneData.Count; + int nBoneCount = CachedBoneData.Length; AccumulatedBoneMask |= boneMask; @@ -597,11 +597,11 @@ public override bool SetupBones(Span boneToWorldOut, int maxBones, in } if (!boneToWorldOut.IsEmpty) { - if (maxBones >= CachedBoneData.Count) { + if (maxBones >= CachedBoneData.Length) { memcpy(boneToWorldOut, CachedBoneData.AsSpan()); } else { - Warning($"SetupBones: invalid bone array size ({maxBones} - needs {CachedBoneData.Count})\n"); + Warning($"SetupBones: invalid bone array size ({maxBones} - needs {CachedBoneData.Length})\n"); return false; } } @@ -992,12 +992,12 @@ private static void RecvProxy_Sequence(ref readonly RecvProxyData data, object i Studio.DestroyBoneCache(HitboxBoneCacheHandle); HitboxBoneCacheHandle = 0; - if (CachedBoneData.Count != hdr.NumBones()) { - CachedBoneData.SetSize(hdr.NumBones()); + if (CachedBoneData.Length != hdr.NumBones()) { + CachedBoneData = new Matrix3x4[hdr.NumBones()]; for (i = 0; i < hdr.NumBones(); i++) - MathLib.SetIdentityMatrix(out CachedBoneData.AsSpan()[i]); + MathLib.SetIdentityMatrix(out CachedBoneData[i]); } - BoneAccessor.Init(CachedBoneData.Base()); + BoneAccessor.Init(CachedBoneData); Ik = null; diff --git a/Source.Common/BoneSetup.cs b/Source.Common/BoneSetup.cs index d1491285d..58d92a485 100644 --- a/Source.Common/BoneSetup.cs +++ b/Source.Common/BoneSetup.cs @@ -1545,7 +1545,7 @@ public void CalcAutoplaySequences(Span pos, Span q, TimeUni ikContext?.AddAutoplayLocks(pos, q); - int count = studioHdr.GetAutoplayList(out Span pList); + int count = studioHdr.GetAutoplayList(out ReadOnlySpan pList); for (int i = 0; i < count; i++) { int sequenceIndex = pList[i]; MStudioSeqDesc seqdesc = studioHdr.Seqdesc(sequenceIndex); diff --git a/Source.Common/DataCache/IMDLCache.cs b/Source.Common/DataCache/IMDLCache.cs index 221250d77..66626e4a3 100644 --- a/Source.Common/DataCache/IMDLCache.cs +++ b/Source.Common/DataCache/IMDLCache.cs @@ -51,7 +51,7 @@ public interface IMDLCache VCollide? GetVCollide(MDLHandle_t handle); Memory GetAnimBlock(MDLHandle_t handle, int block); VirtualModel? GetVirtualModel(MDLHandle_t handle); - Memory GetAutoplayList(MDLHandle_t handle); + ReadOnlySpan GetAutoplayList(MDLHandle_t handle); VertexFileHeader? GetVertexData(MDLHandle_t handle); void TouchAllData(MDLHandle_t handle); diff --git a/Source.Common/Engine/IModelInfo.cs b/Source.Common/Engine/IModelInfo.cs index 01177d6b7..aafe256e3 100644 --- a/Source.Common/Engine/IModelInfo.cs +++ b/Source.Common/Engine/IModelInfo.cs @@ -61,7 +61,7 @@ public interface IVModelInfo byte ComputeViewScreenFade(in Vector3 absOrigin, float radius, float fadeScale); // both client and server - int GetAutoplayList(StudioHeader studioHdr, out Span autoplayList); + int GetAutoplayList(StudioHeader studioHdr, out ReadOnlySpan autoplayList); // Gets a terrain collision model (creates if necessary) // NOTE: This may return NULL if the terrain model cannot be ized diff --git a/Source.Common/Exts.cs b/Source.Common/Exts.cs index e8ed40f1f..7b8a8b7c1 100644 --- a/Source.Common/Exts.cs +++ b/Source.Common/Exts.cs @@ -650,51 +650,7 @@ public static class BitVecExts public static void Clear(this bool[] bools, int bit) => bools[bit] = false; } public static class ListExtensions -{ // Thanks JamesHoux: https://stackoverflow.com/questions/4972951/listt-to-t-without-copying - delegate ref TField RefAccessor(TObj obj); - static class ArrayAccessor - { - public static Func, T[]> Getter; - public static RefAccessor, int> GetCount; - - static ArrayAccessor() { - var dm = new DynamicMethod("get", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T[]), [typeof(List)], typeof(ArrayAccessor), true); - var il = dm.GetILGenerator(); - il.Emit(OpCodes.Ldarg_0); // Load List argument - il.Emit(OpCodes.Ldfld, typeof(List).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)!); // Replace argument by field - il.Emit(OpCodes.Ret); // Return field - Getter = (Func, T[]>)dm.CreateDelegate(typeof(Func, T[]>)); - - dm = new DynamicMethod("getSize", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(int).MakeByRefType(), [typeof(List)], typeof(ArrayAccessor), true); - il = dm.GetILGenerator(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldflda, typeof(List).GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance)!); - il.Emit(OpCodes.Ret); - GetCount = (RefAccessor, int>)dm.CreateDelegate(typeof(RefAccessor, int>)); - } - } - - static class TempSwap - { - public static readonly ThreadLocal> List = new(() => []); - } - - public static void Swap(this List a, List b) { - var swap = TempSwap.List.Value!; - swap.Clear(); - swap.EnsureCapacity(a.Count); - - a.Clear(); - a.EnsureCapacity(b.Count); - b.Base().AsSpan()[..b.Count].CopyTo(a.Base()); - a.CountForEdit() = b.Count; - - b.Clear(); - b.EnsureCapacity(swap.Count); - swap.Base().AsSpan()[..swap.Count].CopyTo(b.Base()); - b.CountForEdit() = swap.Count; - } - +{ public static nint Find(this List list, T? value) { for (int i = 0, c = list.Count; i < c; i++) { T? at = list[i]; @@ -710,14 +666,6 @@ public static nint Find(this List list, T? value) { } return -1; } - - public static T[] Base(this List list) { - return ArrayAccessor.Getter(list); - } - - public static ref int CountForEdit(this List list) { - return ref ArrayAccessor.GetCount(list); - } } public static class ClassUtils { diff --git a/Source.Common/IStudioRender.cs b/Source.Common/IStudioRender.cs index d7fd8d065..0b3826b3f 100644 --- a/Source.Common/IStudioRender.cs +++ b/Source.Common/IStudioRender.cs @@ -2,6 +2,7 @@ using Source.Common.Engine; using Source.Common.MaterialSystem; using Source.Common.Mathematics; +using Source.Common.Utilities; using System.Numerics; @@ -100,8 +101,8 @@ public interface IStudioRender { bool LoadModel(StudioHeader studioHDR, Memory vtxData, StudioHWData hardwareData); void UnloadModel(StudioHWData hardwareData); - int GetMaterialList(StudioHeader studioHDR, Span materials); - int GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, Span outputMaterials); + void GetMaterialList(StudioHeader studioHDR, ref SpanList materials); + void GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, ref SpanList outputMaterials); Span LockBoneMatrices(int boneCount); void UnlockBoneMatrices(); void LockFlexWeights(int weightCount, out Span flexWeights); diff --git a/Source.Common/Studio.cs b/Source.Common/Studio.cs index ce047d563..86762d582 100644 --- a/Source.Common/Studio.cs +++ b/Source.Common/Studio.cs @@ -343,14 +343,14 @@ public VirtualGroup AnimGroup(int animation) { public readonly object Lock = new(); - public readonly List Seq = []; - public readonly List Anim = []; - public readonly List Attachment = []; - public readonly List Pose = []; + public List Seq = []; + public List Anim = []; + public List Attachment = []; + public List Pose = []; public readonly List Group = []; - public readonly List Node = []; - public readonly List IKLock = []; - public readonly List AutoplaySequences = []; + public List Node = []; + public List IKLock = []; + public short[] AutoplaySequences = []; } public enum StudioMeshGroupFlags @@ -2038,7 +2038,7 @@ public class StudioHdr public Span Bones() => studioHdr.Bones(); - readonly List StudioHdrCache = []; + StudioHeader?[] StudioHdrCache = []; public StudioHdrFlags Flags() => studioHdr!.Flags; public AnonymousSafeFieldPointer FrameUnlockCounterPtr; @@ -2048,7 +2048,7 @@ public void Init(StudioHeader? studioHdr, IMDLCache mdlcache) { this.studioHdr = studioHdr; this.vModel = null; - StudioHdrCache.Clear(); + StudioHdrCache = []; if (this.studioHdr == null) return; @@ -2088,10 +2088,7 @@ public bool SequencesAvailable() { private VirtualModel? ResetVModel(VirtualModel? virtualModel) { if (virtualModel != null) { vModel = virtualModel; - StudioHdrCache!.EnsureCountDefault(vModel.Group.Count()); - - for (int i = 0; i < StudioHdrCache.Count; i++) - StudioHdrCache[i] = null!; + StudioHdrCache = new StudioHeader?[vModel.Group.Count()]; return vModel; } @@ -2120,13 +2117,13 @@ public int RemapSeqBone(int sequence, int localBone) { lock (FrameUnlockCounterMutex) { if (FrameUnlockCounterPtr.Get() != FrameUnlockCounter) // i.e., this thread got the mutex { - memcreset(StudioHdrCache.Base()); + memcreset(StudioHdrCache); FrameUnlockCounter = FrameUnlockCounterPtr.Get(); } } } - if (!StudioHdrCache.IsValidIndex(i)) { + if ((uint)i >= (uint)StudioHdrCache.Length) { ReadOnlySpan name = (this.studioHdr != null) ? this.studioHdr.GetName() : "<>"; Debugger.Break(); return this.studioHdr; // return something known to probably exist, certainly things will be messed up, but hopefully not crash before the warning is noticed @@ -2674,7 +2671,7 @@ internal int RelativeSeq(int baseseq, int relseq) { return group.MasterSeq[relseq]; } - public int GetAutoplayList(out Span pList) { + public int GetAutoplayList(out ReadOnlySpan pList) { return studioHdr!.GetAutoplayList(out pList); } @@ -3181,7 +3178,7 @@ public ref byte LocalTransition(int i) { } - public int GetAutoplayList(out Span pList) { + public int GetAutoplayList(out ReadOnlySpan pList) { return modelinfo.GetAutoplayList(this, out pList); } @@ -3221,18 +3218,6 @@ public int CountAutoplaySequences() { return count; } - public void CopyAutoplaySequences(ref Memory autoplaySequenceList, int outCount) { - int outIndex = 0; - for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) { - MStudioSeqDesc seqdesc = Seqdesc(i); - if ((seqdesc.Flags & StudioAnimSeqFlags.Autoplay) != 0) { - autoplaySequenceList.Span[outIndex] = (short)i; - outIndex++; - } - } - autoplaySequenceList = autoplaySequenceList[..outIndex]; - } - public int CopyAutoplaySequences(Span autoplaySequenceList, int outCount) { int outIndex = 0; for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) { diff --git a/Source.Common/StudioVirtualModel.cs b/Source.Common/StudioVirtualModel.cs index 3a973efb8..cb17adb39 100644 --- a/Source.Common/StudioVirtualModel.cs +++ b/Source.Common/StudioVirtualModel.cs @@ -56,16 +56,8 @@ public void Dispose() { private int LookupIndex; } - void BeginTemporaryCopyList(List source, out List dest) { - dest = ListPool.Shared.Alloc(); - dest.Clear(); - dest.CountForEdit() = source.Count; - source.AsSpan().CopyTo(dest.AsSpan()); - } - void EndTemporaryCopyList(List source, List dest) { - source.Swap(dest); - ListPool.Shared.Free(dest); - } + void BeginTemporaryCopyList(List source, out List dest) => dest = new List(source); + void EndTemporaryCopyList(ref List source, List dest) => source = dest; public void AppendSequences(int group, StudioHeader studioHdr) { lock (Lock) { @@ -123,7 +115,7 @@ public void AppendSequences(int group, StudioHeader studioHdr) { GetSeqTable()[s1.Hash(invariant: false)] = (short)j; } } - EndTemporaryCopyList(Seq, seq); + EndTemporaryCopyList(ref Seq, seq); } } } @@ -171,7 +163,7 @@ public void AppendAnimations(int group, StudioHeader studioHdr) { } } - EndTemporaryCopyList(Anim, anim); + EndTemporaryCopyList(ref Anim, anim); } } } @@ -224,7 +216,7 @@ public void AppendAttachments(int group, StudioHeader studioHdr) { Group[group].MasterAttachment[j] = k; } - EndTemporaryCopyList(Attachment, attachment); + EndTemporaryCopyList(ref Attachment, attachment); } } public void AppendPoseParameters(int group, StudioHeader studioHdr) { @@ -264,7 +256,7 @@ public void AppendPoseParameters(int group, StudioHeader studioHdr) { Group[group].MasterPose[j] = k; } - EndTemporaryCopyList(Pose, pose); + EndTemporaryCopyList(ref Pose, pose); } } public void AppendBonemap(int group, StudioHeader studioHdr) { @@ -339,7 +331,7 @@ public void AppendNodes(int group, StudioHeader studioHdr) { Group[group].MasterNode[j] = k; } - EndTemporaryCopyList(Node, node); + EndTemporaryCopyList(ref Node, node); } } @@ -367,7 +359,7 @@ public void AppendIKLocks(int group, StudioHeader studioHDR) { } } - EndTemporaryCopyList(IKLock, iklock); + EndTemporaryCopyList(ref IKLock, iklock); } } @@ -426,7 +418,8 @@ public void AppendModels(int group, StudioHeader studioHdr) { } public void UpdateAutoplaySequences(StudioHeader studioHdr) { int autoplayCount = studioHdr.CountAutoplaySequences(); - AutoplaySequences.SetCount(autoplayCount); - studioHdr.CopyAutoplaySequences(AutoplaySequences.Base().AsSpan(), autoplayCount); + if (AutoplaySequences.Length != autoplayCount) + AutoplaySequences = new short[autoplayCount]; + studioHdr.CopyAutoplaySequences(AutoplaySequences, autoplayCount); } } diff --git a/Source.Common/Utilities/SpanList.cs b/Source.Common/Utilities/SpanList.cs new file mode 100644 index 000000000..e9cd69e21 --- /dev/null +++ b/Source.Common/Utilities/SpanList.cs @@ -0,0 +1,26 @@ +namespace Source.Common.Utilities; + +/// +/// style of operations on fixed/pre-allocated arrays. +/// +public ref struct SpanList(Span items) +{ + readonly Span Items = items; + int Used; + + public readonly int Count => Used; + public readonly int Capacity => Items.Length; + + public readonly ref T this[int index] => ref Items[..Used][index]; + + public void Add(in T value) => Items[Used++] = value; + + public void RemoveAt(int index) { + Items[(index + 1)..Used].CopyTo(Items[index..]); + Used--; + } + + public void Clear() => Used = 0; + + public readonly Span AsSpan() => Items[..Used]; +} diff --git a/Source.DataCache/MDLCache.cs b/Source.DataCache/MDLCache.cs index 20164c3ee..0900528d6 100644 --- a/Source.DataCache/MDLCache.cs +++ b/Source.DataCache/MDLCache.cs @@ -45,7 +45,7 @@ public class StudioData public object? UserData; - public Memory AutoplaySequenceList; + public short[] AutoplaySequenceList = []; public int AnimBlockCount; public object?[]? AnimBlock; // todo: research what this is @@ -218,13 +218,13 @@ public bool GetAsyncLoad(MDLCacheDataType type) { throw new NotImplementedException(); } - public Memory GetAutoplayList(MDLHandle_t handle) { + public ReadOnlySpan GetAutoplayList(MDLHandle_t handle) { if (handle == MDLHANDLE_INVALID) - return Array.Empty(); + return []; VirtualModel? virtualModel = GetVirtualModel(handle); if (virtualModel != null) - return virtualModel.AutoplaySequences.Base(); + return virtualModel.AutoplaySequences; StudioData studioData = HandleToMDLDict[handle]; return studioData.AutoplaySequenceList; @@ -415,7 +415,7 @@ private bool ReadMDLFile(MDLHandle_t handle, ReadOnlySpan mdlFileName, Mem int count = studioHdr.CountAutoplaySequences(); if (count != 0) { AllocateAutoplaySequences(HandleToMDLDict[handle], count); - studioHdr.CopyAutoplaySequences(ref HandleToMDLDict[handle].AutoplaySequenceList, count); + studioHdr.CopyAutoplaySequences(HandleToMDLDict[handle].AutoplaySequenceList, count); } } diff --git a/Source.Engine/CModel_Private.cs b/Source.Engine/CModel_Private.cs index 1d6576f63..cf8bd4536 100644 --- a/Source.Engine/CModel_Private.cs +++ b/Source.Engine/CModel_Private.cs @@ -13,7 +13,7 @@ using System.Runtime.Intrinsics; using TraceCounter_t = uint; -using TraceCounterVec = System.Collections.Generic.List; +using TraceCounterVec = uint[]; namespace Source.Engine; @@ -54,8 +54,14 @@ public void Reset() { } public bool CheckPrimary; public int CheckDepth = -1; public readonly TraceCounter_t[] Count = new TraceCounter_t[MAX_CHECK_COUNT_DEPTH]; - public readonly TraceCounterVec[] BrushCounters = new TraceCounterVec[MAX_CHECK_COUNT_DEPTH].InstantiateArray(); - public readonly TraceCounterVec[] DispCounters = new TraceCounterVec[MAX_CHECK_COUNT_DEPTH].InstantiateArray(); + public readonly TraceCounterVec[] BrushCounters = NewCounters(); + public readonly TraceCounterVec[] DispCounters = NewCounters(); + + static TraceCounterVec[] NewCounters() { + TraceCounterVec[] counters = new TraceCounterVec[MAX_CHECK_COUNT_DEPTH]; + Array.Fill(counters, []); + return counters; + } public TraceCounter_t GetCount() => Count[CheckDepth]; public Span GetBrushCounters() => BrushCounters[CheckDepth].AsSpan(); @@ -368,16 +374,13 @@ public static void DestroyDispPhysCollide() { internal static TraceInfo BeginTrace() { TraceInfo traceInfo = g_TraceInfoPool.Alloc(); - if (traceInfo.BrushCounters[0].Count != GetCollisionBSPData().NumBrushes + 1) { + if (traceInfo.BrushCounters[0].Length != GetCollisionBSPData().NumBrushes + 1) { memreset(traceInfo.Count); traceInfo.CheckDepth = -1; for (int i = 0; i < MAX_CHECK_COUNT_DEPTH; i++) { - traceInfo.BrushCounters[i].SetCount(GetCollisionBSPData().NumBrushes + 1); - traceInfo.DispCounters[i].SetCount((int)g_DispCollTreeCount); - - memreset(traceInfo.BrushCounters[i].AsSpan()); - memreset(traceInfo.DispCounters[i].AsSpan()); + traceInfo.BrushCounters[i] = new TraceCounter_t[GetCollisionBSPData().NumBrushes + 1]; + traceInfo.DispCounters[i] = new TraceCounter_t[g_DispCollTreeCount]; } } @@ -673,8 +676,8 @@ internal static void PushTraceVisits(TraceInfo traceInfo) { traceInfo.Count[i]++; if (traceInfo.Count[i] == 0) { traceInfo.Count[i]++; - memreset(traceInfo.BrushCounters[i].Base()); - memreset(traceInfo.DispCounters[i].Base()); + memreset(traceInfo.BrushCounters[i]); + memreset(traceInfo.DispCounters[i]); } } @@ -1269,15 +1272,15 @@ internal static void TraceToLeaf(bool IS_POINT, TraceInfo traceInfo, int ndxLeaf // trace ray/box sweep against all brushes in this leaf int numleafbrushes = leaf.NumLeafBrushes; int lastleafbrush = leaf.FirstLeafBrush + numleafbrushes; - List map_leafbrushes = traceInfo.BSPData!.MapLeafBrushes; - List map_brushes = traceInfo.BSPData!.MapBrushes; + ushort[] map_leafbrushes = traceInfo.BSPData!.MapLeafBrushes; + CollisionBrush[] map_brushes = traceInfo.BSPData!.MapBrushes; Span counters = traceInfo.GetBrushCounters(); TraceCounter_t count = traceInfo.GetCount(); for (int ndxLeafBrush = leaf.FirstLeafBrush; ndxLeafBrush < lastleafbrush; ndxLeafBrush++) { // get the current brush - int ndxBrush = map_leafbrushes.AsSpan()[ndxLeafBrush]; + int ndxBrush = map_leafbrushes[ndxLeafBrush]; - ref CollisionBrush brush = ref map_brushes.AsSpan()[ndxBrush]; + ref CollisionBrush brush = ref map_brushes[ndxBrush]; // make sure we only check this brush once per trace/stab if (!traceInfo.Visit(ref brush, ndxBrush, count, counters)) diff --git a/Source.Engine/CollisionModelSubsystem.cs b/Source.Engine/CollisionModelSubsystem.cs index c53d8d8ad..ca23efcf1 100644 --- a/Source.Engine/CollisionModelSubsystem.cs +++ b/Source.Engine/CollisionModelSubsystem.cs @@ -8,6 +8,7 @@ using Source.Common.GUI; using Source.Common.MaterialSystem; using Source.Common.Mathematics; +using Source.Common.Utilities; using System.Drawing; using System.Numerics; @@ -27,16 +28,16 @@ public class CollisionBSPData { public string? MapName; public string? MapNullName; - public readonly List MapCollisionModels = []; - public readonly List MapSurfaces = []; - public readonly List MapPlanes = []; - public readonly List MapBrushes = []; - public readonly List MapBrushSides = []; - public readonly List MapBoxBrushes = []; - public readonly List MapNodes = []; - public readonly List MapLeafs = []; - public readonly List MapDispList = []; - public readonly List MapLeafBrushes = []; + public CollisionModel[] MapCollisionModels = []; + public CollisionSurface[] MapSurfaces = []; + public CollisionPlane[] MapPlanes = []; + public CollisionBrush[] MapBrushes = []; + public CollisionBrushSide[] MapBrushSides = []; + public CollisionBoxBrush[] MapBoxBrushes = []; + public CollisionNode[] MapNodes = []; + public CollisionLeaf[] MapLeafs = []; + public ushort[] MapDispList = []; + public ushort[] MapLeafBrushes = []; public readonly List TextureNames = []; public static readonly CollisionSurface NullSurface = new() { Name = "**empty**", Flags = 0, SurfaceProps = 0 }; public string? MapEntityString; @@ -61,9 +62,9 @@ public class CollisionBSPData // More of these should just be explicit Count's into their respective lists, honestly public int NumSurfaces; public int NumLeafs; - public int NumBrushes => MapBrushes.Count; + public int NumBrushes => MapBrushes.Length; public int NumAreas; - public int NumPlanes => MapPlanes.Count; + public int NumPlanes => MapPlanes.Length; public int NumClusters; public int NumNodes; public int NumTextures; @@ -90,7 +91,7 @@ internal void PreLoad() { Init(); } internal void Destroy() { - for (int i = 0; i < MapCollisionModels.Count; i++) + for (int i = 0; i < MapCollisionModels.Length; i++) physcollision.VCollideUnload(MapCollisionModels[i].VCollisionData); // DispCollTrees_FreeLeafList(this); @@ -100,20 +101,21 @@ internal void Destroy() { // CM.g_DispBounds = null; CM.g_DispCollTreeCount = 0; - MapPlanes.Clear(); + MapPlanes = []; TextureNames.Clear(); - MapSurfaces.Clear(); + MapSurfaces = []; MapAreaPortals = []; PortalOpen = []; MapAreas = []; MapEntityString = null; - MapBrushes.Clear(); - MapDispList.Clear(); - MapCollisionModels.Clear(); - MapLeafBrushes.Clear(); - MapLeafs.Clear(); - MapNodes.Clear(); - MapBrushSides.Clear(); + MapBrushes = []; + MapDispList = []; + MapCollisionModels = []; + MapLeafBrushes = []; + MapLeafs = []; + MapNodes = []; + MapBrushSides = []; + MapBoxBrushes = []; MapVis = null; NumBrushSides = 0; @@ -139,7 +141,8 @@ internal void LoadTextures() { BSPDTexData[] inData = lh.LoadLumpData(throwIfNoElements: true, maxElements: BSPFileCommon.MAX_MAP_TEXDATA, sysErrorIfOOB: true); IMaterial? material; - MapSurfaces.Clear(); MapSurfaces.EnsureCapacity(inData.Length); + MapSurfaces = new CollisionSurface[inData.Length]; + SpanList surfaces = new(MapSurfaces); TextureNames.Clear(); TextureNames.EnsureCapacity(inData.Length); int lastNull = -1; for (int i = 0; i < stringData.Length; i++) { @@ -158,7 +161,7 @@ internal void LoadTextures() { int index = _in.NameStringTableID; - MapSurfaces.Add(new CollisionSurface() { + surfaces.Add(new CollisionSurface() { Name = TextureNames[index]!, SurfaceProps = 0, Flags = 0 @@ -211,7 +214,7 @@ internal void LoadLeafs() { private void CollisionBSPData_LoadLeafs_Version_1(MapLoadHelper lh) { BSPDLeaf[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_LEAFS, sysErrorIfOOB: true); int count = inData.Length; - MapLeafs.Clear(); MapLeafs.EnsureCount(count + 1); + MapLeafs = new CollisionLeaf[count + 1]; NumLeafs = count; NumClusters = 0; @@ -247,7 +250,7 @@ private void CollisionBSPData_LoadLeafs_Version_1(MapLoadHelper lh) { private void CollisionBSPData_LoadLeafs_Version_0(MapLoadHelper lh) { BSPDLeafVersion0[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_PLANES, sysErrorIfOOB: true); int count = inData.Length; - MapLeafs.Clear(); MapLeafs.EnsureCount(count + 1); + MapLeafs = new CollisionLeaf[count + 1]; NumLeafs = count; NumClusters = 0; @@ -284,13 +287,13 @@ internal void LoadLeafBrushes() { MapLoadHelper lh = new MapLoadHelper(LumpIndex.LeafBrushes); ushort[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_LEAFBRUSHES, sysErrorIfOOB: true); - MapLeafBrushes.Clear(); MapLeafBrushes.AddRange(inData); + MapLeafBrushes = inData; } internal void LoadPlanes() { MapLoadHelper lh = new MapLoadHelper(LumpIndex.Planes); BSPDPlane[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_PLANES, sysErrorIfOOB: true); - MapPlanes.Clear(); MapPlanes.EnsureCount(inData.Length + 1); + MapPlanes = new CollisionPlane[inData.Length + 1]; Span planes = MapPlanes.AsSpan(); int count = inData.Length; @@ -313,7 +316,7 @@ internal void LoadBrushes() { MapLoadHelper lh = new MapLoadHelper(LumpIndex.Brushes); BSPDBrush[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_BRUSHES, sysErrorIfOOB: true); - MapBrushes.EnsureCountNew(inData.Length); + MapBrushes = new CollisionBrush[inData.Length]; Span outData = MapBrushes.AsSpan(); for (int i = 0; i < inData.Length; i++) { ref CollisionBrush outBrush = ref outData[i]; @@ -354,8 +357,8 @@ internal void LoadBrushSides(List map_texinfo) { } } - MapBrushSides.EnsureCountNew(brushSideCount); - MapBoxBrushes.EnsureCountNew(boxBrushCount); + MapBrushSides = new CollisionBrushSide[brushSideCount]; + MapBoxBrushes = new CollisionBoxBrush[boxBrushCount]; Span brushsides = MapBrushSides.AsSpan(); Span boxbrushes = MapBoxBrushes.AsSpan(); @@ -377,7 +380,7 @@ internal void LoadBrushSides(List map_texinfo) { for (int j = 0; j < brush.NumSides; j++) { ref CollisionBrushSide side = ref brushsides[outBrushSide]; ref BSPDBrushSide inputSide = ref inData[firstInputSide + j]; - side.SetPlanePointer(MapPlanes.Base(), inputSide.PlaneNum); + side.SetPlanePointer(MapPlanes, inputSide.PlaneNum); int t = inputSide.TexInfo; if (t >= map_texinfo.Count) Sys.Error("Bad brushside texinfo"); @@ -435,10 +438,10 @@ internal void LoadSubmodels() { MapLoadHelper lh = new MapLoadHelper(LumpIndex.Models); BSPDModel[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_MODELS, sysErrorIfOOB: true); - MapCollisionModels.EnsureCountNew(inData.Length); + MapCollisionModels = new CollisionModel[inData.Length]; for (int i = 0; i < inData.Length; i++) { - CollisionModel outModel = MapCollisionModels[i]; + CollisionModel outModel = MapCollisionModels[i] = new(); ref BSPDModel inModel = ref inData[i]; for (int j = 0; j < 3; j++) { // spread the mins / maxs by a pixel @@ -453,7 +456,7 @@ internal void LoadNodes() { MapLoadHelper lh = new MapLoadHelper(LumpIndex.Nodes); BSPDNode[] inData = lh.LoadLumpData(throwIfNoElements: true, BSPFileCommon.MAX_MAP_NODES, sysErrorIfOOB: true); int count = inData.Length; - MapNodes.Clear(); MapNodes.EnsureCount(count + 6); + MapNodes = new CollisionNode[count + 6]; NumNodes = count; MapRootNode = 0; @@ -704,7 +707,7 @@ private void CM_DispTreeLeafnum() { } // Write the compact per-leaf reference lists. - MapDispList.SetCount(totalRefs); + MapDispList = new ushort[totalRefs]; Span outList = MapDispList.AsSpan(); for (int i = 0; i < treeCount; i++) { foreach (int leafIndex in perDispLeaves[i]) { @@ -895,7 +898,7 @@ private static void DispTreeLeafnum(CollisionBSPData bspData) { internal static CollisionModel? InlineModelNumber(int index) { CollisionBSPData bspData = GetCollisionBSPData(); - if ((index < 0) || (index >= bspData.MapCollisionModels.Count)) + if ((index < 0) || (index >= bspData.MapCollisionModels.Length)) return null; return (bspData.MapCollisionModels[index]); diff --git a/Source.Engine/DebugLeafVis.cs b/Source.Engine/DebugLeafVis.cs index 94d8d1cfe..4ee863ec5 100644 --- a/Source.Engine/DebugLeafVis.cs +++ b/Source.Engine/DebugLeafVis.cs @@ -322,7 +322,7 @@ static void RecomputeClipbrushes(bool bEnabled) { int lastBrush = pBSP.NumBrushes; - if (pBSP.MapCollisionModels.Count > 1) + if (pBSP.MapCollisionModels.Length > 1) lastBrush = FindMinBrush(pBSP, pBSP.MapCollisionModels[1].HeadNode, lastBrush); for (int i = 0; i < lastBrush; i++) { diff --git a/Source.Engine/DispInfo.cs b/Source.Engine/DispInfo.cs index 9c7024779..3311fb97d 100644 --- a/Source.Engine/DispInfo.cs +++ b/Source.Engine/DispInfo.cs @@ -25,8 +25,8 @@ public class DispInfo : DispUtilsHelper, IDispInfo public readonly MeshReader MeshReader = new(); - public readonly List Indices = []; - public readonly List Verts = []; + public ushort[] Indices = []; + public DispRenderVert[] Verts = []; public MaxDispVertsBitSet ActiveVerts; public MaxDispVertsBitSet AllowedVerts; @@ -259,7 +259,7 @@ public void CopyMapDispData(in BSPDDispInfo buildDisp) { PowerInfo = PowerInfo.GetPowerInfo(Power); int size = GetSideLength(); - Indices.SetSize(6 * (size - 1) * (size - 1)); + Array.Resize(ref Indices, 6 * (size - 1) * (size - 1)); NodeInfo = new DispNodeInfo[PowerInfo.NodeCount]; } @@ -311,7 +311,7 @@ internal void CopyCoreDispVertData(CoreDispInfo coreDisp, float bumpSTexCoordOff if (NumLightmaps() <= 1) bumpSTexCoordOffset = 0.0f; - Verts.SetSizeInitialized(PowerInfo!.MaxVerts); + Array.Resize(ref Verts, PowerInfo!.MaxVerts); BumpSTexCoordOffset = bumpSTexCoordOffset; for (int i = 0; i < NumVerts(); i++) { ref DispRenderVert vert = ref GetVertex(i); @@ -426,7 +426,7 @@ public void InitializeActiveVerts() { public ref DispRenderVert GetVertex(int i) { Assert(i < NumVerts()); - return ref Verts.AsSpan()[i]; + return ref Verts[i]; } // public void ComputeLightmapAndTextureCoordinate(in RayDispOutput output, Vector2[]? luv, Vector2[]? tuv) => throw new NotImplementedException(); diff --git a/Source.Engine/DispMapload.cs b/Source.Engine/DispMapload.cs index 729a0cf76..1c80655ee 100644 --- a/Source.Engine/DispMapload.cs +++ b/Source.Engine/DispMapload.cs @@ -340,7 +340,7 @@ static unsafe void SetupMeshReaders(Model world, nint numDisplacements) { desc.Vertex.TangentSSize = sizeof(DispRenderVert); desc.Vertex.TangentTSize = sizeof(DispRenderVert); - DispRenderVert[] pBaseVert = pDisp.Verts.Base(); + DispRenderVert[] pBaseVert = pDisp.Verts; desc.Index.IndexSize = 1; @@ -391,10 +391,10 @@ public static bool DispInfo_LoadDisplacements(Model world, MaterialSystem_SortIn int curVert = 0; int curTri = 0; - List coreDisps = []; + CoreDispInfo[] coreDisps = new CoreDispInfo[numDisplacements]; int disp = 0; for (disp = 0; disp < numDisplacements; disp++) - coreDisps.Add(new()); + coreDisps[disp] = new(); for (disp = 0; disp < numDisplacements; ++disp) { ref BSPDDispInfo mapDisp = ref tempDisps[disp]; @@ -413,7 +413,7 @@ public static bool DispInfo_LoadDisplacements(Model world, MaterialSystem_SortIn return false; } - SmoothDispSurfNormals(coreDisps.Base(), numDisplacements); + SmoothDispSurfNormals(coreDisps, numDisplacements); for (disp = 0; disp < numDisplacements; ++disp) { DispInfo_CreateStaticBuffersAndTags(world, disp, coreDisps[disp], tempVerts); diff --git a/Source.Engine/ModelInfo.cs b/Source.Engine/ModelInfo.cs index e1a5b49b3..464c9d5f0 100644 --- a/Source.Engine/ModelInfo.cs +++ b/Source.Engine/ModelInfo.cs @@ -99,9 +99,9 @@ public virtual MDLHandle_t GetCacheHandle(Model model) { public Memory GetAnimBlock(StudioHeader studioHdr, int block) { return mdlCache.GetAnimBlock(studioHdr.VirtualModel, block); } - public int GetAutoplayList(StudioHeader studioHdr, out Span autoplayList) { + public int GetAutoplayList(StudioHeader studioHdr, out ReadOnlySpan autoplayList) { MDLHandle_t handle = studioHdr.VirtualModel; - autoplayList = mdlCache.GetAutoplayList(handle).Span; + autoplayList = mdlCache.GetAutoplayList(handle); return autoplayList.Length; } diff --git a/Source.Engine/ModelLoader.cs b/Source.Engine/ModelLoader.cs index 446bed500..5f5e4c40c 100644 --- a/Source.Engine/ModelLoader.cs +++ b/Source.Engine/ModelLoader.cs @@ -12,8 +12,8 @@ using Source.Common.MaterialSystem; using Source.Common.Mathematics; using Source.Common.ShaderAPI; +using Source.Common.Utilities; -using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -219,10 +219,11 @@ private void ComputeModelFlags(Model model, uint handle) { if ((studioHdr.Flags & StudioHdrFlags.DoNotCastShadows) != 0) model.Flags |= ModelFlag.DoNotCastShadows; - Span materials = new IMaterial[128]; - int materialCount = modelLoader.Mod_GetModelMaterials(model, materials); + InlineArray128 materialBuffer = default; + SpanList materials = new(materialBuffer); + modelLoader.Mod_GetModelMaterials(model, ref materials); - for (int i = 0; i < materialCount; ++i) { + for (int i = 0; i < materials.Count; ++i) { IMaterial material = materials[i]; if (material == null) continue; @@ -525,14 +526,13 @@ private void Studio_LoadModel(Model model, bool touchAllData) { double t1 = Platform.Time; model.Materials = default; - IMaterial[] materials = ArrayPool.Shared.Rent(128); - int nMaterials = Mod_GetModelMaterials(model, materials); + InlineArray128 materialBuffer = default; + SpanList materials = new(materialBuffer); + Mod_GetModelMaterials(model, ref materials); + int nMaterials = materials.Count; - if ((model.LoadFlags & ModelLoaderFlags.Dynamic) != 0) { - model.Materials = new Memory(new IMaterial[nMaterials]); - for (int i = 0; i < nMaterials; i++) - model.Materials.Span[i] = materials[i]; - } + if ((model.LoadFlags & ModelLoaderFlags.Dynamic) != 0) + model.Materials = materials.AsSpan().ToArray(); if (nMaterials != 0) model.LoadFlags |= ModelLoaderFlags.TouchedMaterials; @@ -542,8 +542,6 @@ private void Studio_LoadModel(Model model, bool touchAllData) { if (touchAllData || preloaded) Mod_TouchAllData(model, Host.GetServerCount()); - - ArrayPool.Shared.Return(materials, true); } } @@ -576,34 +574,36 @@ internal void Mod_RecomputeTranslucency(Model mod, int skin, int body, object? c if ((studioHdr.Flags & StudioHdrFlags.ForceOpaque) != 0) return; - IMaterial[] materialsArray = ArrayPool.Shared.Rent(128); - try { - Span materials = materialsArray.AsSpan(0, 128); - int materialCount = StudioRender.GetMaterialListFromBodyAndSkin(mod.Studio, skin, body, materials); - for (int i = 0; i < materialCount; i++) { - if (materials[i] != null) { - using MatRenderContextPtr renderContext = new(SourceDllMain.materials); - renderContext.Bind(materials[i], clientRenderable); - bool isTranslucent = materials[i].IsTranslucent(); - - if (isTranslucent) { - mod.Flags |= ModelFlag.Translucent; - break; - } + InlineArray128 materialBuffer = default; + SpanList materials = new(materialBuffer); + StudioRender.GetMaterialListFromBodyAndSkin(mod.Studio, skin, body, ref materials); + for (int i = 0; i < materials.Count; i++) { + if (materials[i] != null) { + using MatRenderContextPtr renderContext = new(SourceDllMain.materials); + renderContext.Bind(materials[i], clientRenderable); + bool isTranslucent = materials[i].IsTranslucent(); + + if (isTranslucent) { + mod.Flags |= ModelFlag.Translucent; + break; } } } - finally { - ArrayPool.Shared.Return(materialsArray, true); - } } break; } } - internal int Mod_GetModelMaterials(Model model, Span materials) { + static bool ContainsMaterial(ReadOnlySpan materials, IMaterial? material) { + foreach (IMaterial existing in materials) { + if (existing == material) + return true; + } + return false; + } + + internal void Mod_GetModelMaterials(Model model, ref SpanList materials) { StudioHeader studioHdr; - int found = 0; int i; switch (model.Type) { @@ -615,27 +615,26 @@ internal int Mod_GetModelMaterials(Model model, Span materials) { IMaterial? material = MSurf_TexInfo(ref surfID, model.Brush.Shared).Material; - int j = found; - while (--j >= 0) { - if (materials[j] == material) - break; - } - if (j < 0) - materials[found++] = material!; + if (!ContainsMaterial(materials.AsSpan(), material)) + materials.Add(material!); - if (found >= materials.Length) - return found; + if (materials.Count >= materials.Capacity) + return; } } break; case ModelType.Studio: if (!model.Materials.IsEmpty) { - model.Materials.Span.CopyTo(materials); + foreach (IMaterial material in model.Materials.Span) { + if (materials.Count >= materials.Capacity) + break; + materials.Add(material); + } } else { studioHdr = MDLCache.GetStudioHdr(model.Studio); - found = StudioRender.GetMaterialList(studioHdr, materials); + StudioRender.GetMaterialList(studioHdr, ref materials); } break; @@ -643,8 +642,6 @@ internal int Mod_GetModelMaterials(Model model, Span materials) { Assert(false); break; } - - return found; } private void Mod_TouchAllData(Model model, int serverCount) { @@ -1604,8 +1601,8 @@ private void Mod_LoadLeafs() { pMap.Leafs[i].DispCount = pCLeaf[i].DispCount; pMap.Leafs[i].DispListStart = pCLeaf[i].DispListStart; } - pMap.DispInfoReferences = GetCollisionBSPData().MapDispList.Base(); - pMap.NumDispInfoReferences = GetCollisionBSPData().MapDispList.Count; + pMap.DispInfoReferences = GetCollisionBSPData().MapDispList; + pMap.NumDispInfoReferences = GetCollisionBSPData().MapDispList.Length; } private void Mod_LoadMarksurfaces() { @@ -2120,7 +2117,7 @@ private void Mod_LoadVertNormalIndices() { } private void Mod_LoadTexdata() { MapLoadHelper.Map!.NumTexData = GetCollisionBSPData().NumSurfaces; - MapLoadHelper.Map!.TexData = GetCollisionBSPData().MapSurfaces.Base(); + MapLoadHelper.Map!.TexData = GetCollisionBSPData().MapSurfaces; } private void Mod_LoadTexinfo() { @@ -2166,7 +2163,7 @@ private void Mod_LoadTexinfo() { } private void Mod_LoadPlanes() { - MapLoadHelper.Map!.Planes = GetCollisionBSPData().MapPlanes.Base(); + MapLoadHelper.Map!.Planes = GetCollisionBSPData().MapPlanes; MapLoadHelper.Map!.NumPlanes = GetCollisionBSPData().NumPlanes; } diff --git a/Source.StudioRender/StudioRenderContext.cs b/Source.StudioRender/StudioRenderContext.cs index fb9873e47..5b1d72da7 100644 --- a/Source.StudioRender/StudioRenderContext.cs +++ b/Source.StudioRender/StudioRenderContext.cs @@ -6,6 +6,7 @@ using Source.Common.Engine; using Source.Common.MaterialSystem; using Source.Common.Mathematics; +using Source.Common.Utilities; using System; using System.Collections.Generic; @@ -58,12 +59,18 @@ public void EndFrame() { public void UpdateConfig(in StudioRenderConfig config) => RC.Config = config; - public int GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, Span outputMaterials) { - int found = 0; + static bool ContainsMaterial(ReadOnlySpan materials, IMaterial material) { + foreach (IMaterial existing in materials) { + if (existing == material) + return true; + } + return false; + } + public void GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, ref SpanList outputMaterials) { StudioHWData? studioHWData = mdlcache.GetHardwareData(studio); if (studioHWData == null) - return 0; + return; for (int lodID = studioHWData.RootLOD; lodID < studioHWData.NumLODs; lodID++) { StudioHeader studioHdr = mdlcache.GetStudioHdr(studio)!; @@ -82,36 +89,28 @@ public int GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body IMaterial material = inputMaterials[skinRef[mesh.Material]]; Assert(material != null); - int m; - for (m = 0; m < found; m++) { - if (outputMaterials[m] == material) - break; - } - if (m >= found) { - outputMaterials[found++] = material!; + if (!ContainsMaterial(outputMaterials.AsSpan(), material!)) { + outputMaterials.Add(material!); - if (found >= outputMaterials.Length) - return found; + if (outputMaterials.Count >= outputMaterials.Capacity) + return; } } } } - - return found; } - public int GetMaterialList(StudioHeader studioHDR, Span materials) { + public void GetMaterialList(StudioHeader studioHDR, ref SpanList materials) { AssertMsg(studioHDR != null, "Don't ignore this assert! StudioRenderContext.GetMaterialList() has null studioHDR."); if (studioHDR == null) - return 0; + return; if (studioHDR.TextureIndex == 0) - return 0; + return; int i; int j; - int found = 0; Span path = stackalloc char[MAX_PATH]; for (i = 0; i < studioHDR.NumTextures; i++) { path.Clear(); @@ -140,21 +139,13 @@ public int GetMaterialList(StudioHeader studioHDR, Span materials) { if (material == null) continue; - if (found < materials.Length) { - int k; - - for (k = 0; k < found; k++) - if (materials[k] == material) - break; - - if (k >= found) - materials[found++] = material; + if (materials.Count < materials.Capacity) { + if (!ContainsMaterial(materials.AsSpan(), material)) + materials.Add(material); } else break; } - - return found; } public bool LoadModel(StudioHeader studioHdr, Memory vtxBuffer, StudioHWData studioHWData) {