Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Game.Client/C_BaseAnimating.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public void Dispose() {
Vector3 BonePosition;
QAngle BoneAngles;
Handle<C_BaseAnimating> AttachedTo = new();
readonly List<Matrix3x4> CachedBoneData = [];
Matrix3x4[] CachedBoneData = [];

public void InvalidateBoneCache() {
MostRecentModelBoneCounter = ModelBoneCounter - 1;
Expand Down Expand Up @@ -533,7 +533,7 @@ public override bool SetupBones(Span<Matrix3x4> boneToWorldOut, int maxBones, in
AccumulatedBoneMask = 0;
}

int nBoneCount = CachedBoneData.Count;
int nBoneCount = CachedBoneData.Length;

AccumulatedBoneMask |= boneMask;

Expand Down Expand Up @@ -597,11 +597,11 @@ public override bool SetupBones(Span<Matrix3x4> 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;
}
}
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion Source.Common/BoneSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1545,7 +1545,7 @@ public void CalcAutoplaySequences(Span<Vector3> pos, Span<Quaternion> q, TimeUni

ikContext?.AddAutoplayLocks(pos, q);

int count = studioHdr.GetAutoplayList(out Span<short> pList);
int count = studioHdr.GetAutoplayList(out ReadOnlySpan<short> pList);
for (int i = 0; i < count; i++) {
int sequenceIndex = pList[i];
MStudioSeqDesc seqdesc = studioHdr.Seqdesc(sequenceIndex);
Expand Down
2 changes: 1 addition & 1 deletion Source.Common/DataCache/IMDLCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public interface IMDLCache
VCollide? GetVCollide(MDLHandle_t handle);
Memory<byte> GetAnimBlock(MDLHandle_t handle, int block);
VirtualModel? GetVirtualModel(MDLHandle_t handle);
Memory<short> GetAutoplayList(MDLHandle_t handle);
ReadOnlySpan<short> GetAutoplayList(MDLHandle_t handle);
VertexFileHeader? GetVertexData(MDLHandle_t handle);

void TouchAllData(MDLHandle_t handle);
Expand Down
2 changes: 1 addition & 1 deletion Source.Common/Engine/IModelInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<short> autoplayList);
int GetAutoplayList(StudioHeader studioHdr, out ReadOnlySpan<short> autoplayList);

// Gets a terrain collision model (creates if necessary)
// NOTE: This may return NULL if the terrain model cannot be ized
Expand Down
54 changes: 1 addition & 53 deletions Source.Common/Exts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, TField>(TObj obj);
static class ArrayAccessor<T>
{
public static Func<List<T>, T[]> Getter;
public static RefAccessor<List<T>, int> GetCount;

static ArrayAccessor() {
var dm = new DynamicMethod("get", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T[]), [typeof(List<T>)], typeof(ArrayAccessor<T>), true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load List<T> argument
il.Emit(OpCodes.Ldfld, typeof(List<T>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)!); // Replace argument by field
il.Emit(OpCodes.Ret); // Return field
Getter = (Func<List<T>, T[]>)dm.CreateDelegate(typeof(Func<List<T>, T[]>));

dm = new DynamicMethod("getSize", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(int).MakeByRefType(), [typeof(List<T>)], typeof(ArrayAccessor<T>), true);
il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, typeof(List<T>).GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance)!);
il.Emit(OpCodes.Ret);
GetCount = (RefAccessor<List<T>, int>)dm.CreateDelegate(typeof(RefAccessor<List<T>, int>));
}
}

static class TempSwap<T>
{
public static readonly ThreadLocal<List<T>> List = new(() => []);
}

public static void Swap<T>(this List<T> a, List<T> b) {
var swap = TempSwap<T>.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<T>(this List<T> list, T? value) {
for (int i = 0, c = list.Count; i < c; i++) {
T? at = list[i];
Expand All @@ -710,14 +666,6 @@ public static nint Find<T>(this List<T> list, T? value) {
}
return -1;
}

public static T[] Base<T>(this List<T> list) {
return ArrayAccessor<T>.Getter(list);
}

public static ref int CountForEdit<T>(this List<T> list) {
return ref ArrayAccessor<T>.GetCount(list);
}
}
public static class ClassUtils
{
Expand Down
5 changes: 3 additions & 2 deletions Source.Common/IStudioRender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Source.Common.Engine;
using Source.Common.MaterialSystem;
using Source.Common.Mathematics;
using Source.Common.Utilities;

using System.Numerics;

Expand Down Expand Up @@ -100,8 +101,8 @@ public interface IStudioRender {
bool LoadModel(StudioHeader studioHDR, Memory<byte> vtxData, StudioHWData hardwareData);
void UnloadModel(StudioHWData hardwareData);

int GetMaterialList(StudioHeader studioHDR, Span<IMaterial> materials);
int GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, Span<IMaterial> outputMaterials);
void GetMaterialList(StudioHeader studioHDR, ref SpanList<IMaterial> materials);
void GetMaterialListFromBodyAndSkin(MDLHandle_t studio, int skin, int body, ref SpanList<IMaterial> outputMaterials);
Span<Matrix3x4> LockBoneMatrices(int boneCount);
void UnlockBoneMatrices();
void LockFlexWeights(int weightCount, out Span<float> flexWeights);
Expand Down
43 changes: 14 additions & 29 deletions Source.Common/Studio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,14 @@ public VirtualGroup AnimGroup(int animation) {

public readonly object Lock = new();

public readonly List<VirtualSequence> Seq = [];
public readonly List<VirtualGeneric> Anim = [];
public readonly List<VirtualGeneric> Attachment = [];
public readonly List<VirtualGeneric> Pose = [];
public List<VirtualSequence> Seq = [];
public List<VirtualGeneric> Anim = [];
public List<VirtualGeneric> Attachment = [];
public List<VirtualGeneric> Pose = [];
public readonly List<VirtualGroup> Group = [];
public readonly List<VirtualGeneric> Node = [];
public readonly List<VirtualGeneric> IKLock = [];
public readonly List<short> AutoplaySequences = [];
public List<VirtualGeneric> Node = [];
public List<VirtualGeneric> IKLock = [];
public short[] AutoplaySequences = [];
}

public enum StudioMeshGroupFlags
Expand Down Expand Up @@ -2038,7 +2038,7 @@ public class StudioHdr
public Span<MStudioBone> Bones() => studioHdr.Bones();


readonly List<StudioHeader?> StudioHdrCache = [];
StudioHeader?[] StudioHdrCache = [];

public StudioHdrFlags Flags() => studioHdr!.Flags;
public AnonymousSafeFieldPointer<int> FrameUnlockCounterPtr;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<StudioHeader>(StudioHdrCache);
FrameUnlockCounter = FrameUnlockCounterPtr.Get();
}
}
}

if (!StudioHdrCache.IsValidIndex(i)) {
if ((uint)i >= (uint)StudioHdrCache.Length) {
ReadOnlySpan<char> name = (this.studioHdr != null) ? this.studioHdr.GetName() : "<<null>>";
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
Expand Down Expand Up @@ -2674,7 +2671,7 @@ internal int RelativeSeq(int baseseq, int relseq) {
return group.MasterSeq[relseq];
}

public int GetAutoplayList(out Span<short> pList) {
public int GetAutoplayList(out ReadOnlySpan<short> pList) {
return studioHdr!.GetAutoplayList(out pList);
}

Expand Down Expand Up @@ -3181,7 +3178,7 @@ public ref byte LocalTransition(int i) {
}


public int GetAutoplayList(out Span<short> pList) {
public int GetAutoplayList(out ReadOnlySpan<short> pList) {
return modelinfo.GetAutoplayList(this, out pList);
}

Expand Down Expand Up @@ -3221,18 +3218,6 @@ public int CountAutoplaySequences() {
return count;
}

public void CopyAutoplaySequences(ref Memory<short> 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<short> autoplaySequenceList, int outCount) {
int outIndex = 0;
for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) {
Expand Down
29 changes: 11 additions & 18 deletions Source.Common/StudioVirtualModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,8 @@ public void Dispose() {
private int LookupIndex;
}

void BeginTemporaryCopyList<T>(List<T> source, out List<T> dest) {
dest = ListPool<T>.Shared.Alloc();
dest.Clear();
dest.CountForEdit() = source.Count;
source.AsSpan().CopyTo(dest.AsSpan());
}
void EndTemporaryCopyList<T>(List<T> source, List<T> dest) {
source.Swap(dest);
ListPool<T>.Shared.Free(dest);
}
void BeginTemporaryCopyList<T>(List<T> source, out List<T> dest) => dest = new List<T>(source);
void EndTemporaryCopyList<T>(ref List<T> source, List<T> dest) => source = dest;

public void AppendSequences(int group, StudioHeader studioHdr) {
lock (Lock) {
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -171,7 +163,7 @@ public void AppendAnimations(int group, StudioHeader studioHdr) {
}
}

EndTemporaryCopyList(Anim, anim);
EndTemporaryCopyList(ref Anim, anim);
}
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -339,7 +331,7 @@ public void AppendNodes(int group, StudioHeader studioHdr) {
Group[group].MasterNode[j] = k;
}

EndTemporaryCopyList(Node, node);
EndTemporaryCopyList(ref Node, node);
}
}

Expand Down Expand Up @@ -367,7 +359,7 @@ public void AppendIKLocks(int group, StudioHeader studioHDR) {
}
}

EndTemporaryCopyList(IKLock, iklock);
EndTemporaryCopyList(ref IKLock, iklock);
}
}

Expand Down Expand Up @@ -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);
}
}
26 changes: 26 additions & 0 deletions Source.Common/Utilities/SpanList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Source.Common.Utilities;

/// <summary>
/// <see cref="List{T}"/> style of operations on fixed/pre-allocated arrays.
/// </summary>
public ref struct SpanList<T>(Span<T> items)
{
readonly Span<T> 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<T> AsSpan() => Items[..Used];
}
Loading
Loading