Skip to content

Commit e5014c5

Browse files
authored
Merge pull request #213 from ZehMatt/mat-queue-mode
Mat queue mode
2 parents c093c80 + 000aa7f commit e5014c5

26 files changed

Lines changed: 2776 additions & 598 deletions

‎Source.Common/ICallQueue.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ public void QueueFunctor(ref Functor functor) {
2020

2121
}
2222
void QueueFunctorInternal(ref Functor functor);
23+
24+
void QueueCall(Action func);
25+
void QueueCall<T>(Action<T> func, in T args);
2326
}

‎Source.Common/Launcher/IGraphicsContext.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public interface IGraphicsContext {
44
nint HardwareHandle { get; }
55

66
void MakeCurrent();
7+
void ReleaseCurrent();
78
void SetSwapInterval(float swapInterval);
89
void SwapBuffers();
910
}

‎Source.Common/MaterialSystem/IMaterialVar.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public abstract class IMaterialVar
4747
protected ITexture? TextureValue;
4848
protected byte NumVectorComps;
4949
protected bool FakeMaterialVar;
50-
protected byte TempIndex;
50+
protected byte TempIndex = 0xFF;
5151
protected UtlSymbol Name = new();
5252
public MaterialVarGPU GPU;
5353

@@ -162,10 +162,10 @@ public Vector4 GetVec4Value() {
162162
protected abstract int VectorSizeInternal();
163163

164164
public int GetIntValue() {
165-
return IntVal;
165+
return TempIndex == 0xFF ? IntVal : GetIntValueInternal();
166166
}
167167
public float GetFloatValue() {
168-
return VecVal.X;
168+
return TempIndex == 0xFF ? VecVal.X : GetFloatValueInternal();
169169
}
170170

171171
public static readonly UtlSymbolTableMT MaterialVarSymbols = new(true);

‎Source.Common/ShaderAPI/IShaderUtil.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ public interface IShaderUtil
66
{
77
bool InFlashlightMode();
88
bool OnDrawMesh(IMesh mesh, int firstIndex, int indexCount);
9+
unsafe bool OnDrawMesh(IMesh mesh, PrimList* lists, int numLists);
10+
bool OnSetFlexMesh(IMesh staticMesh, IMesh? mesh, int vertexOffsetInBytes);
11+
bool OnSetColorMesh(IMesh staticMesh, IMesh? mesh, int vertexOffsetInBytes);
912
bool OnSetPrimitiveType(IMesh mesh, MaterialPrimitiveType type);
1013
bool OnFlushBufferedPrimitives();
1114
void SyncMatrices();
1215
void SyncMatrix(MaterialMatrixMode mode);
1316
void RestoreShaderObjects(IServiceProvider services, int changeFlags = 0);
1417
void BindStandardTexture(Sampler sampler, StandardTextureId id);
15-
}
18+
}

‎Source.Engine/Host.cs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,10 +1311,12 @@ public int CountVariablesWithFlags(FCvar flags, bool nonDefault) {
13111311
return count;
13121312
}
13131313

1314-
internal void AllowQueuedMaterialSystem(bool v) {
1315-
// todo
1314+
internal bool AllowQueuedMaterialSystem(bool v) {
1315+
return services.GetRequiredService<Source.Common.MaterialSystem.IMaterialSystem>().AllowThreading(v, MaterialSystemThread);
13161316
}
13171317

1318+
public static int MaterialSystemThread = 0;
1319+
13181320
[ConCommand(helpText: "Reload the most recent saved game (add setpos to jump to current view position on reload).")]
13191321
void reload(in TokenizedCommand args, CommandSource source, int clientSlot = -1) {
13201322
if (

‎Source.Engine/Render.cs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,25 @@ public void EndUpdateLightmaps() {
600600
if (--LightmapUpdateDepth == 0) {
601601
if (g_LightmapUpdateList.Count != 0 && r_dynamiclighting.GetBool() && !ModelLoader.r_unloadlightmaps.GetBool()) {
602602
DLight[] lights = CL.DLights;
603+
if (CL.ActiveDlights && materials.GetRenderContext().GetCallQueue() != null) {
604+
lights = ThreadDLights[ThreadDLightsFrame];
605+
for (int i = 0; i < lights.Length; i++) {
606+
DLight src = CL.DLights[i], dst = lights[i];
607+
dst.Flags = src.Flags;
608+
dst.Origin = src.Origin;
609+
dst.Radius = src.Radius;
610+
dst.Color = src.Color;
611+
dst.Die = src.Die;
612+
dst.Decay = src.Decay;
613+
dst.MinLight = src.MinLight;
614+
dst.Key = src.Key;
615+
dst.Style = src.Style;
616+
dst.Direction = src.Direction;
617+
dst.InnerAngle = src.InnerAngle;
618+
dst.OuterAngle = src.OuterAngle;
619+
}
620+
ThreadDLightsFrame = (ThreadDLightsFrame + 1) & 1;
621+
}
603622

604623
g_LightmapUpdateList.Sort(LightmapPageComparison ??= LightmapPageCompareFunc);
605624
for (int i = g_LightmapUpdateList.Count - 1; i >= 0; --i) {
@@ -842,6 +861,23 @@ private void BuildLightMap(DLight[] lights, ref BSPMSurface2 surfID, in Matrix3x
842861
if (onlyUseLightStyles)
843862
dlightMask = 0;
844863

864+
ICallQueue? callQueue = materials.GetRenderContext().GetCallQueue();
865+
if (callQueue != null) {
866+
Span<BSPMSurface2> surfaces = host_state.WorldBrush!.Surfaces2;
867+
int surfaceIndex = (int)(Unsafe.ByteOffset(ref surfaces[0], ref surfID) / Unsafe.SizeOf<BSPMSurface2>());
868+
callQueue.QueueCall(static s => s.Render.BuildLightMapGutsQueued(s.Lights, s.SurfaceIndex, in s.XForm, s.Mask, s.Bumpmap, s.Lightmap),
869+
(Render: this, Lights: lights, SurfaceIndex: surfaceIndex, XForm: entityToWorld, Mask: dlightMask, Bumpmap: needsBumpmap, Lightmap: needsLightmap));
870+
return;
871+
}
872+
873+
BuildLightMapGuts(lights, ref surfID, in entityToWorld, dlightMask, needsBumpmap, needsLightmap);
874+
}
875+
876+
static readonly DLight[][] ThreadDLights = [new DLight[Constants.MAX_DLIGHTS].InstantiateArray(), new DLight[Constants.MAX_DLIGHTS].InstantiateArray()];
877+
static int ThreadDLightsFrame;
878+
879+
void BuildLightMapGutsQueued(DLight[] lights, int surfaceIndex, in Matrix3x4 entityToWorld, uint dlightMask, bool needsBumpmap, bool needsLightmap) {
880+
ref BSPMSurface2 surfID = ref host_state.WorldBrush!.Surfaces2[surfaceIndex];
845881
BuildLightMapGuts(lights, ref surfID, in entityToWorld, dlightMask, needsBumpmap, needsLightmap);
846882
}
847883

‎Source.MaterialSystem/IMatRenderContextInternal.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public interface IMatRenderContextInternal : IMatRenderContext
2828

2929
bool OnDrawMesh(IMesh mesh, int firstIndex, int numIndices);
3030
unsafe bool OnDrawMesh(IMesh mesh, PrimList* pLists, int nLists);
31-
bool OnSetFlexMesh(IMesh staticMesh, IMesh mesh, int nVertexOffsetInBytes);
32-
bool OnSetColorMesh(IMesh staticMesh, IMesh mesh, int nVertexOffsetInBytes);
31+
bool OnSetFlexMesh(IMesh staticMesh, IMesh? mesh, int nVertexOffsetInBytes);
32+
bool OnSetColorMesh(IMesh staticMesh, IMesh? mesh, int nVertexOffsetInBytes);
3333
bool OnSetPrimitiveType(IMesh mesh, MaterialPrimitiveType type);
3434
bool OnFlushBufferedPrimitives();
3535

@@ -43,7 +43,7 @@ public interface IMatRenderContextInternal : IMatRenderContext
4343
void SetFrameTime(TimeUnit_t frameTime);
4444
void SetCurrentProxy(object? proxy);
4545
void MarkRenderDataUnused(bool bBeginFrame);
46-
MatCallQueue GetCallQueueInternal();
46+
MatCallQueue? GetCallQueueInternal();
4747

4848
#if GMOD_DLL
4949
bool GMOD_IsLowOnMemory() { return false; }

‎Source.MaterialSystem/IMaterialSystemInternal.cs‎

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,127 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using System.Runtime.CompilerServices;
78
using System.Text;
89

910
namespace Source.MaterialSystem;
1011

1112

12-
public class MatCallQueue : ICallQueue
13+
public sealed class MatCallQueue : ICallQueue
1314
{
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+
14128
public void QueueFunctorInternal(ref Functor functor) {
15129
throw new NotImplementedException();
16130
}
@@ -42,7 +156,7 @@ public interface IMaterialSystemInternal : IMaterialSystem
42156
bool CanUseEditorMaterials();
43157
ReadOnlySpan<char> GetForcedTextureLoadPathID();
44158

45-
MatCallQueue GetRenderCallQueue();
159+
MatCallQueue? GetRenderCallQueue();
46160

47161
void UnbindMaterial(IMaterial? material);
48162
uint GetRenderThreadId();

‎Source.MaterialSystem/MatLightmaps.cs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,22 @@ public void EnableLightmapFiltering(bool enabled) {
410410
public MaterialSystem GetMaterialSystem() => materialSystem;
411411

412412
public void BeginUpdateLightmaps() {
413+
MatCallQueue? callQueue = materialSystem.GetRenderContextInternal().GetCallQueueInternal();
414+
if (callQueue != null) {
415+
callQueue.QueueCall(static lightmaps => lightmaps.BeginUpdateLightmaps(), this);
416+
return;
417+
}
418+
413419
UpdatingLightmapsStackDepth++;
414420
}
415421

416422
public void EndUpdateLightmaps() {
423+
MatCallQueue? callQueue = materialSystem.GetRenderContextInternal().GetCallQueueInternal();
424+
if (callQueue != null) {
425+
callQueue.QueueCall(static lightmaps => lightmaps.EndUpdateLightmaps(), this);
426+
return;
427+
}
428+
417429
UpdatingLightmapsStackDepth--;
418430
Assert(UpdatingLightmapsStackDepth >= 0);
419431
if (UpdatingLightmapsStackDepth <= 0 && LockedLightmap != -1) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace Source.MaterialSystem;
4+
5+
public sealed unsafe class MatMemoryStack(int blockSize, int alignment) : IDisposable
6+
{
7+
struct Block
8+
{
9+
public byte* Base;
10+
public int Size;
11+
public int Used;
12+
}
13+
14+
readonly List<Block> Blocks = [];
15+
int CurrentBlock = -1;
16+
int TotalUsed;
17+
18+
public int GetUsed() => TotalUsed;
19+
20+
static int AlignValue(int value, int alignment) => (value + alignment - 1) & ~(alignment - 1);
21+
22+
public byte* Alloc(int size) {
23+
if (size <= 0)
24+
return null;
25+
26+
int alignedSize = AlignValue(size, alignment);
27+
28+
while (CurrentBlock >= 0 && CurrentBlock < Blocks.Count) {
29+
Block block = Blocks[CurrentBlock];
30+
if (block.Size - block.Used >= alignedSize) {
31+
byte* result = block.Base + block.Used;
32+
block.Used += alignedSize;
33+
Blocks[CurrentBlock] = block;
34+
TotalUsed += alignedSize;
35+
return result;
36+
}
37+
38+
if (CurrentBlock + 1 >= Blocks.Count)
39+
break;
40+
CurrentBlock++;
41+
}
42+
43+
int newSize = Math.Max(blockSize, alignedSize);
44+
Block newBlock = new() {
45+
Base = (byte*)NativeMemory.AlignedAlloc((nuint)newSize, (nuint)alignment),
46+
Size = newSize,
47+
Used = alignedSize
48+
};
49+
Blocks.Add(newBlock);
50+
CurrentBlock = Blocks.Count - 1;
51+
TotalUsed += alignedSize;
52+
return newBlock.Base;
53+
}
54+
55+
public void ShrinkLast(byte* ptr, int oldSize, int newSize) {
56+
if (ptr == null || CurrentBlock < 0)
57+
return;
58+
59+
Block block = Blocks[CurrentBlock];
60+
int oldAligned = AlignValue(oldSize, alignment);
61+
if (block.Base + block.Used - oldAligned != ptr)
62+
return;
63+
64+
int newAligned = AlignValue(newSize, alignment);
65+
block.Used -= oldAligned - newAligned;
66+
TotalUsed -= oldAligned - newAligned;
67+
Blocks[CurrentBlock] = block;
68+
}
69+
70+
public void FreeAll() {
71+
for (int i = 0; i < Blocks.Count; i++) {
72+
Block block = Blocks[i];
73+
block.Used = 0;
74+
Blocks[i] = block;
75+
}
76+
CurrentBlock = Blocks.Count > 0 ? 0 : -1;
77+
TotalUsed = 0;
78+
}
79+
80+
public void Term() {
81+
for (int i = 0; i < Blocks.Count; i++)
82+
NativeMemory.AlignedFree(Blocks[i].Base);
83+
Blocks.Clear();
84+
CurrentBlock = -1;
85+
TotalUsed = 0;
86+
}
87+
88+
public void Dispose() => Term();
89+
}

0 commit comments

Comments
 (0)