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
3 changes: 3 additions & 0 deletions Source.Common/ICallQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ public void QueueFunctor(ref Functor functor) {

}
void QueueFunctorInternal(ref Functor functor);

void QueueCall(Action func);
void QueueCall<T>(Action<T> func, in T args);
}
1 change: 1 addition & 0 deletions Source.Common/Launcher/IGraphicsContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public interface IGraphicsContext {
nint HardwareHandle { get; }

void MakeCurrent();
void ReleaseCurrent();
void SetSwapInterval(float swapInterval);
void SwapBuffers();
}
6 changes: 3 additions & 3 deletions Source.Common/MaterialSystem/IMaterialVar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public abstract class IMaterialVar
protected ITexture? TextureValue;
protected byte NumVectorComps;
protected bool FakeMaterialVar;
protected byte TempIndex;
protected byte TempIndex = 0xFF;
protected UtlSymbol Name = new();
public MaterialVarGPU GPU;

Expand Down Expand Up @@ -162,10 +162,10 @@ public Vector4 GetVec4Value() {
protected abstract int VectorSizeInternal();

public int GetIntValue() {
return IntVal;
return TempIndex == 0xFF ? IntVal : GetIntValueInternal();
}
public float GetFloatValue() {
return VecVal.X;
return TempIndex == 0xFF ? VecVal.X : GetFloatValueInternal();
}

public static readonly UtlSymbolTableMT MaterialVarSymbols = new(true);
Expand Down
5 changes: 4 additions & 1 deletion Source.Common/ShaderAPI/IShaderUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ public interface IShaderUtil
{
bool InFlashlightMode();
bool OnDrawMesh(IMesh mesh, int firstIndex, int indexCount);
unsafe bool OnDrawMesh(IMesh mesh, PrimList* lists, int numLists);
bool OnSetFlexMesh(IMesh staticMesh, IMesh? mesh, int vertexOffsetInBytes);
bool OnSetColorMesh(IMesh staticMesh, IMesh? mesh, int vertexOffsetInBytes);
bool OnSetPrimitiveType(IMesh mesh, MaterialPrimitiveType type);
bool OnFlushBufferedPrimitives();
void SyncMatrices();
void SyncMatrix(MaterialMatrixMode mode);
void RestoreShaderObjects(IServiceProvider services, int changeFlags = 0);
void BindStandardTexture(Sampler sampler, StandardTextureId id);
}
}
6 changes: 4 additions & 2 deletions Source.Engine/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,10 +1311,12 @@ public int CountVariablesWithFlags(FCvar flags, bool nonDefault) {
return count;
}

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

public static int MaterialSystemThread = 0;

[ConCommand(helpText: "Reload the most recent saved game (add setpos to jump to current view position on reload).")]
void reload(in TokenizedCommand args, CommandSource source, int clientSlot = -1) {
if (
Expand Down
36 changes: 36 additions & 0 deletions Source.Engine/Render.cs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,25 @@ public void EndUpdateLightmaps() {
if (--LightmapUpdateDepth == 0) {
if (g_LightmapUpdateList.Count != 0 && r_dynamiclighting.GetBool() && !ModelLoader.r_unloadlightmaps.GetBool()) {
DLight[] lights = CL.DLights;
if (CL.ActiveDlights && materials.GetRenderContext().GetCallQueue() != null) {
lights = ThreadDLights[ThreadDLightsFrame];
for (int i = 0; i < lights.Length; i++) {
DLight src = CL.DLights[i], dst = lights[i];
dst.Flags = src.Flags;
dst.Origin = src.Origin;
dst.Radius = src.Radius;
dst.Color = src.Color;
dst.Die = src.Die;
dst.Decay = src.Decay;
dst.MinLight = src.MinLight;
dst.Key = src.Key;
dst.Style = src.Style;
dst.Direction = src.Direction;
dst.InnerAngle = src.InnerAngle;
dst.OuterAngle = src.OuterAngle;
}
ThreadDLightsFrame = (ThreadDLightsFrame + 1) & 1;
}

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

ICallQueue? callQueue = materials.GetRenderContext().GetCallQueue();
if (callQueue != null) {
Span<BSPMSurface2> surfaces = host_state.WorldBrush!.Surfaces2;
int surfaceIndex = (int)(Unsafe.ByteOffset(ref surfaces[0], ref surfID) / Unsafe.SizeOf<BSPMSurface2>());
callQueue.QueueCall(static s => s.Render.BuildLightMapGutsQueued(s.Lights, s.SurfaceIndex, in s.XForm, s.Mask, s.Bumpmap, s.Lightmap),
(Render: this, Lights: lights, SurfaceIndex: surfaceIndex, XForm: entityToWorld, Mask: dlightMask, Bumpmap: needsBumpmap, Lightmap: needsLightmap));
return;
}

BuildLightMapGuts(lights, ref surfID, in entityToWorld, dlightMask, needsBumpmap, needsLightmap);
}

static readonly DLight[][] ThreadDLights = [new DLight[Constants.MAX_DLIGHTS].InstantiateArray(), new DLight[Constants.MAX_DLIGHTS].InstantiateArray()];
static int ThreadDLightsFrame;

void BuildLightMapGutsQueued(DLight[] lights, int surfaceIndex, in Matrix3x4 entityToWorld, uint dlightMask, bool needsBumpmap, bool needsLightmap) {
ref BSPMSurface2 surfID = ref host_state.WorldBrush!.Surfaces2[surfaceIndex];
BuildLightMapGuts(lights, ref surfID, in entityToWorld, dlightMask, needsBumpmap, needsLightmap);
}

Expand Down
6 changes: 3 additions & 3 deletions Source.MaterialSystem/IMatRenderContextInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public interface IMatRenderContextInternal : IMatRenderContext

bool OnDrawMesh(IMesh mesh, int firstIndex, int numIndices);
unsafe bool OnDrawMesh(IMesh mesh, PrimList* pLists, int nLists);
bool OnSetFlexMesh(IMesh staticMesh, IMesh mesh, int nVertexOffsetInBytes);
bool OnSetColorMesh(IMesh staticMesh, IMesh mesh, int nVertexOffsetInBytes);
bool OnSetFlexMesh(IMesh staticMesh, IMesh? mesh, int nVertexOffsetInBytes);
bool OnSetColorMesh(IMesh staticMesh, IMesh? mesh, int nVertexOffsetInBytes);
bool OnSetPrimitiveType(IMesh mesh, MaterialPrimitiveType type);
bool OnFlushBufferedPrimitives();

Expand All @@ -43,7 +43,7 @@ public interface IMatRenderContextInternal : IMatRenderContext
void SetFrameTime(TimeUnit_t frameTime);
void SetCurrentProxy(object? proxy);
void MarkRenderDataUnused(bool bBeginFrame);
MatCallQueue GetCallQueueInternal();
MatCallQueue? GetCallQueueInternal();

#if GMOD_DLL
bool GMOD_IsLowOnMemory() { return false; }
Expand Down
118 changes: 116 additions & 2 deletions Source.MaterialSystem/IMaterialSystemInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,127 @@

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace Source.MaterialSystem;


public class MatCallQueue : ICallQueue
public sealed class MatCallQueue : ICallQueue
{
abstract class CallBuffer
{
public bool Used;
public abstract void Execute(int index);
public abstract void Clear();
}

sealed class ActionBuffer : CallBuffer
{
Action[] Funcs = new Action[64];
int Count;

public int Add(Action func) {
if (Count == Funcs.Length)
Array.Resize(ref Funcs, Count * 2);
Funcs[Count] = func;
return Count++;
}

public override void Execute(int index) => Funcs[index]();

public override void Clear() {
Array.Clear(Funcs, 0, Count);
Count = 0;
}
}

sealed class CallBuffer<T> : CallBuffer
{
Action<T>[] Funcs = new Action<T>[16];
T[] Args = new T[16];
int Count;

public int Add(Action<T> func, in T args) {
if (Count == Args.Length) {
Array.Resize(ref Funcs, Count * 2);
Array.Resize(ref Args, Count * 2);
}
Funcs[Count] = func;
Args[Count] = args;
return Count++;
}

public override void Execute(int index) => Funcs[index](Args[index]);

public override void Clear() {
Array.Clear(Funcs, 0, Count);
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
Array.Clear(Args, 0, Count);
Count = 0;
}
}

struct Entry
{
public CallBuffer Buffer;
public int Index;
}

static int NextTypeID = 1;
static class TypeID<T>
{
public static readonly int Value = Interlocked.Increment(ref NextTypeID) - 1;
}

readonly ActionBuffer Actions = new();
CallBuffer?[] Buffers = new CallBuffer?[64];
readonly List<CallBuffer> UsedBuffers = [];
Entry[] Entries = new Entry[4096];
int EntryCount;

public int Count() => EntryCount;

void AddEntry(CallBuffer buffer, int index) {
if (!buffer.Used) {
buffer.Used = true;
UsedBuffers.Add(buffer);
}
if (EntryCount == Entries.Length)
Array.Resize(ref Entries, EntryCount * 2);
ref Entry entry = ref Entries[EntryCount++];
entry.Buffer = buffer;
entry.Index = index;
}

public void QueueCall(Action func) => AddEntry(Actions, Actions.Add(func));

public void QueueCall<T>(Action<T> func, in T args) {
int id = TypeID<T>.Value;
if (id >= Buffers.Length)
Array.Resize(ref Buffers, Math.Max(id + 1, Buffers.Length * 2));
CallBuffer<T> buffer = (CallBuffer<T>?)Buffers[id] ?? (CallBuffer<T>)(Buffers[id] = new CallBuffer<T>());
AddEntry(buffer, buffer.Add(func, in args));
}

public void CallQueued() {
for (int i = 0; i < EntryCount; i++) {
ref Entry entry = ref Entries[i];
entry.Buffer.Execute(entry.Index);
}
Flush();
}

public void Flush() {
Array.Clear(Entries, 0, EntryCount);
EntryCount = 0;
for (int i = 0; i < UsedBuffers.Count; i++) {
UsedBuffers[i].Clear();
UsedBuffers[i].Used = false;
}
UsedBuffers.Clear();
}

public void QueueFunctorInternal(ref Functor functor) {
throw new NotImplementedException();
}
Expand Down Expand Up @@ -42,7 +156,7 @@ public interface IMaterialSystemInternal : IMaterialSystem
bool CanUseEditorMaterials();
ReadOnlySpan<char> GetForcedTextureLoadPathID();

MatCallQueue GetRenderCallQueue();
MatCallQueue? GetRenderCallQueue();

void UnbindMaterial(IMaterial? material);
uint GetRenderThreadId();
Expand Down
12 changes: 12 additions & 0 deletions Source.MaterialSystem/MatLightmaps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,22 @@ public void EnableLightmapFiltering(bool enabled) {
public MaterialSystem GetMaterialSystem() => materialSystem;

public void BeginUpdateLightmaps() {
MatCallQueue? callQueue = materialSystem.GetRenderContextInternal().GetCallQueueInternal();
if (callQueue != null) {
callQueue.QueueCall(static lightmaps => lightmaps.BeginUpdateLightmaps(), this);
return;
}

UpdatingLightmapsStackDepth++;
}

public void EndUpdateLightmaps() {
MatCallQueue? callQueue = materialSystem.GetRenderContextInternal().GetCallQueueInternal();
if (callQueue != null) {
callQueue.QueueCall(static lightmaps => lightmaps.EndUpdateLightmaps(), this);
return;
}

UpdatingLightmapsStackDepth--;
Assert(UpdatingLightmapsStackDepth >= 0);
if (UpdatingLightmapsStackDepth <= 0 && LockedLightmap != -1) {
Expand Down
89 changes: 89 additions & 0 deletions Source.MaterialSystem/MatMemoryStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Runtime.InteropServices;

namespace Source.MaterialSystem;

public sealed unsafe class MatMemoryStack(int blockSize, int alignment) : IDisposable
{
struct Block
{
public byte* Base;
public int Size;
public int Used;
}

readonly List<Block> Blocks = [];
int CurrentBlock = -1;
int TotalUsed;

public int GetUsed() => TotalUsed;

static int AlignValue(int value, int alignment) => (value + alignment - 1) & ~(alignment - 1);

public byte* Alloc(int size) {
if (size <= 0)
return null;

int alignedSize = AlignValue(size, alignment);

while (CurrentBlock >= 0 && CurrentBlock < Blocks.Count) {
Block block = Blocks[CurrentBlock];
if (block.Size - block.Used >= alignedSize) {
byte* result = block.Base + block.Used;
block.Used += alignedSize;
Blocks[CurrentBlock] = block;
TotalUsed += alignedSize;
return result;
}

if (CurrentBlock + 1 >= Blocks.Count)
break;
CurrentBlock++;
}

int newSize = Math.Max(blockSize, alignedSize);
Block newBlock = new() {
Base = (byte*)NativeMemory.AlignedAlloc((nuint)newSize, (nuint)alignment),
Comment thread
ZehMatt marked this conversation as resolved.
Size = newSize,
Used = alignedSize
};
Blocks.Add(newBlock);
CurrentBlock = Blocks.Count - 1;
TotalUsed += alignedSize;
return newBlock.Base;
}

public void ShrinkLast(byte* ptr, int oldSize, int newSize) {
if (ptr == null || CurrentBlock < 0)
return;

Block block = Blocks[CurrentBlock];
int oldAligned = AlignValue(oldSize, alignment);
if (block.Base + block.Used - oldAligned != ptr)
return;

int newAligned = AlignValue(newSize, alignment);
block.Used -= oldAligned - newAligned;
TotalUsed -= oldAligned - newAligned;
Blocks[CurrentBlock] = block;
}

public void FreeAll() {
for (int i = 0; i < Blocks.Count; i++) {
Block block = Blocks[i];
block.Used = 0;
Blocks[i] = block;
}
CurrentBlock = Blocks.Count > 0 ? 0 : -1;
TotalUsed = 0;
}

public void Term() {
for (int i = 0; i < Blocks.Count; i++)
NativeMemory.AlignedFree(Blocks[i].Base);
Blocks.Clear();
CurrentBlock = -1;
TotalUsed = 0;
}

public void Dispose() => Term();
}
Loading
Loading