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
28 changes: 14 additions & 14 deletions Snooper/Core/Containers/Buffers/Buffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public abstract class Buffer<T>(BufferTarget target, BufferUsageHint usageHint,
public int Count { get; private set; }
public int Capacity { get; private set; }

private bool _bInitialized;
protected bool IsAllocated;
private readonly Dictionary<int, BufferAllocationMetadata> _allocations = new();
private readonly SortedSet<FreeBlock> _freeBlocks = new(Comparer<FreeBlock>.Create((a, b) =>
{
Expand All @@ -52,12 +52,12 @@ public abstract class Buffer<T>(BufferTarget target, BufferUsageHint usageHint,

public override void Generate()
{
if (_bInitialized)
if (IsAllocated)
throw new InvalidOperationException("Buffer is already initialized.");

GL.CreateBuffers(1, out uint handle);
Handle = handle;
_bInitialized = false;
IsAllocated = false;
}

public void Bind()
Expand All @@ -78,11 +78,11 @@ private void ResizeIfNeeded(int newSize, double factor = 1.5, bool copy = false)
var oldCapacity = Capacity;
Capacity = (int) Math.Max(Capacity * factor, newSize);

if (_bInitialized)
if (IsAllocated)
{
Log.Warning("Resizing buffer {0} ({1}) from {2} to {3} (asked: {4}) (initialized!!!!!!)", Handle, PName, oldCapacity, Capacity, newSize);

_bInitialized = false;
IsAllocated = false;
if (copy)
{
var oldBuffer = Handle;
Expand All @@ -109,15 +109,15 @@ private void ResizeIfNeeded(int newSize, double factor = 1.5, bool copy = false)

public void Reallocate(int size)
{
_bInitialized = false;
IsAllocated = false;
Allocate(size);
}

public void Allocate(uint size) => Allocate((int)size);
public void Allocate(int size)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);
if (_bInitialized)
if (IsAllocated)
throw new InvalidOperationException("Buffer is already initialized. Use Update method to modify data.");

if (size > Capacity)
Expand All @@ -133,7 +133,7 @@ public void Allocate(int size)
// _allocationIdCounter = 0;
// _allocations.Clear();
// _freeBlocks.Clear();
_bInitialized = true;
IsAllocated = true;
}

public BufferAllocation Add(T data) => AddInternal([data]);
Expand All @@ -143,7 +143,7 @@ private BufferAllocation AddInternal(T[] data)
var length = data.Length;
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length);

if (!_bInitialized)
if (!IsAllocated)
{
Allocate(length);
}
Expand Down Expand Up @@ -176,7 +176,7 @@ private void UpsertInternal(int index, T[] data)
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length);
ArgumentOutOfRangeException.ThrowIfNegative(index);

if (!_bInitialized)
if (!IsAllocated)
{
Allocate(index + length);
}
Expand All @@ -196,7 +196,7 @@ private void UpsertInternal(int index, T[] data)
public void Update(int allocationId, T[] data) => UpdateInternal(allocationId, data);
private void UpdateInternal(int allocationId, T[] data, bool batched = false)
{
if (!_bInitialized)
if (!IsAllocated)
throw new InvalidOperationException("Buffer is not initialized. Use Add method to initialize it.");

if (!_allocations.TryGetValue(allocationId, out var metadata))
Expand All @@ -215,7 +215,7 @@ private void UpdateInternal(int allocationId, T[] data, bool batched = false)
public void UpdateCustom<TCustom>(BufferAllocation allocation, TCustom data, int offset) where TCustom : unmanaged => UpdateCustomInternal(allocation.AllocationId, data, offset);
private void UpdateCustomInternal<TCustom>(int allocationId, TCustom data, int offset) where TCustom : unmanaged
{
if (!_bInitialized)
if (!IsAllocated)
throw new InvalidOperationException("Buffer is not initialized. Use Add method to initialize it.");

if (!_allocations.TryGetValue(allocationId, out var metadata))
Expand Down Expand Up @@ -304,7 +304,7 @@ public BufferAllocation CopyFrom(Buffer<T> sourceBuffer, BufferAllocation source
var length = sourceAllocation.Length;
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length);

if (!_bInitialized)
if (!IsAllocated)
{
Allocate(length);
}
Expand All @@ -326,7 +326,7 @@ public BufferAllocation CopyFrom(Buffer<T> sourceBuffer, BufferAllocation source

public void Clear()
{
if (!_bInitialized)
if (!IsAllocated)
throw new InvalidOperationException("Cannot clear a buffer that is not initialized.");

ClearStorage(0, TotalElements * Stride);
Expand Down
6 changes: 5 additions & 1 deletion Snooper/Core/Containers/Buffers/ShaderStorageBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ public sealed class ShaderStorageBuffer<T>(BufferUsageHint usageHint = BufferUsa

public void Bind(uint index)
{
GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, index, Handle);
// iGPUs don't like binding unallocated buffers, and unlike some other buffers, we never unbind SSBOs
// silently skipping the bind leaves whatever another system bound at this index in place
// that's too risky, so we unbind the slot instead so an unallocated buffer reads as out of range
// TODO: we should properly unbind SSBOs, and skip if unallocated
GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, index, IsAllocated ? Handle : 0);
}

public void QueueUpdate(BufferAllocation allocation, T data) => _batcher.Add(allocation, data);
Expand Down
3 changes: 2 additions & 1 deletion Snooper/Core/Containers/Programs/EmbeddedShader.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Reflection;
using OpenTK.Graphics.OpenGL4;
using Snooper.Core.Containers.Buffers;
using Snooper.Core.Hardware;

namespace Snooper.Core.Containers.Programs;

Expand All @@ -25,7 +26,7 @@ protected override uint CompileShader(ShaderType type, string file)
content = string.Join("\n", Defines.Select(d => $"#define {d}")) + "\n" + content;
}

content = string.Join('\n', "#version 460 core", "", Bindings.GlslDefines, "", content);
content = string.Join('\n', "#version 460 core", "", Bindings.GlslDefines, DeviceInfo.GlslDefines, content);

return base.CompileShader(type, content);
}
Expand Down
58 changes: 50 additions & 8 deletions Snooper/Core/Containers/Resources/GeometryPool.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Numerics;
using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.UE4.Objects.Core.Misc;
using OpenTK.Graphics.OpenGL4;
using Snooper.Core.Containers.Buffers;
using Snooper.Rendering.Components.Camera;
using Snooper.Core.Hardware;
using Snooper.Rendering.Components.Descriptors;

namespace Snooper.Core.Containers.Resources;
Expand All @@ -18,6 +17,51 @@ public class GeometryHandle(uint firstIndex, uint baseVertex, BufferAllocation m
public int OverrideLod { get; internal set; } = overrideLod;
}

public readonly struct VertexArrayLayout
{
private readonly uint _vao;
private readonly uint _vbo;
private readonly int _stride;

public VertexArrayLayout(uint vao, uint vbo, int stride)
{
_vao = vao;
_vbo = vbo;
_stride = stride;

if (!DeviceInfo.IsIntel)
{
GL.VertexArrayVertexBuffer(vao, 0, vbo, 0, stride);
}
}

public VertexArrayLayout Float(uint location, int size, VertexAttribType type = VertexAttribType.Float, bool normalized = false, uint offset = 0)
{
GL.VertexArrayAttribFormat(_vao, location, size, type, normalized, DeviceInfo.IsIntel ? 0 : offset);
return Enable(location, offset);
}

public VertexArrayLayout Integer(uint location, int size, VertexAttribIType type = VertexAttribIType.UnsignedInt, uint offset = 0)
{
GL.VertexArrayAttribIFormat(_vao, location, size, type, DeviceInfo.IsIntel ? 0 : offset);
return Enable(location, offset);
}

private VertexArrayLayout Enable(uint location, uint offset)
{
var binding = 0u;
if (DeviceInfo.IsIntel)
{
binding = location;
GL.VertexArrayVertexBuffer(_vao, binding, _vbo, (nint)offset, _stride);
}

GL.VertexArrayAttribBinding(_vao, location, binding);
GL.EnableVertexArrayAttrib(_vao, location);
return this;
}
}

public class GeometryPool<TVertex> : IMemoryDetailsProvider, IDisposable where TVertex : unmanaged
{
private readonly VertexArray _vao = new();
Expand All @@ -27,7 +71,7 @@ public class GeometryPool<TVertex> : IMemoryDetailsProvider, IDisposable where T
private readonly CullingResources _culling = new();

private readonly Dictionary<FGuid, GeometryHandle> _cache = new();
private Action<uint>? _vertexLayoutSetter;
private Action<VertexArrayLayout>? _vertexLayoutSetter;

public void Generate()
{
Expand All @@ -41,18 +85,16 @@ public void Generate()
_vbo.OnHandleChanged += (_, _) => BindBuffersToVao();
}

public void SetVertexLayout(Action<uint> setter)
public void SetVertexLayout(Action<VertexArrayLayout> setter)
{
_vertexLayoutSetter = setter;
BindBuffersToVao();
}

private void BindBuffersToVao()
{
GL.VertexArrayVertexBuffer(_vao, 0, _vbo, 0, _vbo.Stride);
GL.VertexArrayElementBuffer(_vao, _ebo);

_vertexLayoutSetter?.Invoke(_vao);
_vertexLayoutSetter?.Invoke(new VertexArrayLayout(_vao, _vbo, _vbo.Stride));
}

public void Allocate(AllocationCounts counts)
Expand Down
2 changes: 1 addition & 1 deletion Snooper/Core/Containers/Resources/IndirectResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Generate()
_materialData.Generate();
}

public void SetVertexLayout(Action<uint> setter) => _geometry.SetVertexLayout(setter);
public void SetVertexLayout(Action<VertexArrayLayout> setter) => _geometry.SetVertexLayout(setter);

public void Allocate(AllocationCounts counts)
{
Expand Down
6 changes: 6 additions & 0 deletions Snooper/Core/Hardware/DeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ public class DeviceInfo
public ExtensionSupport ExtensionSupport { get; } = new();
public GpuMemoryInfo Memory { get; } = new();

public static bool IsIntel { get; private set; }
public static string GlslDefines { get; private set; } = string.Empty;

public void Load()
{
Name = GL.GetString(StringName.Renderer);
Vendor = GL.GetString(StringName.Vendor);
MaxShaderStorageBufferBindings = GL.GetInteger(GetPName.MaxShaderStorageBufferBindings);
ExtensionSupport.Load();
Memory.Load(ExtensionSupport);

IsIntel = Vendor.Contains("Intel", StringComparison.OrdinalIgnoreCase);
GlslDefines = IsIntel ? "#define BINDLESS_RAW_HANDLES\n" : string.Empty;
}
}
9 changes: 9 additions & 0 deletions Snooper/Core/Managers/ActorManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ private void DequeueSystems(int limit = 0)

Systems.Add(system.Order, system);
system.Load();

if (system is IResizable resizable)
resizable.Resize(_width, _height); // resize right away for the screen-sized resources to get allocated (ClusteredLightSystem)

count++;
}
}
Expand Down Expand Up @@ -232,8 +236,13 @@ private void TrackBackgroundWork()
}
}

private int _width;
private int _height;
public virtual void Resize(int newWidth, int newHeight)
{
_width = newWidth;
_height = newHeight;

foreach (var system in Systems.Values.OfType<IResizable>())
system.Resize(newWidth, newHeight);
}
Expand Down
9 changes: 5 additions & 4 deletions Snooper/Rendering/Components/Mesh/MeshComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace Snooper.Rendering.Components.Mesh;
/// <summary>
/// Packed vertex layout — 20 bytes total<br/>
/// loc 0: uvec2 — pos.x|pos.y (half2), pos.z|0 (half2) [offset 0, 8 bytes]<br/>
/// loc 1: uint — normal xyzw RGB10A2 SNorm [offset 8, 4 bytes]<br/>
/// loc 1: uint — normal xyz RGB10A2 SNorm, w = basis sign [offset 8, 4 bytes]<br/>
/// loc 2: uint — tangent xyz RGB10A2 SNorm, w = texLayer(0-3) [offset 12, 4 bytes]<br/>
/// loc 3: uint — uv.x|uv.y (half2) [offset 16, 4 bytes]<br/>
/// </summary>
Expand All @@ -43,10 +43,11 @@ private static uint PackHalf2(float x, float y)
return hx | (hy << 16);
}

private static uint PackRgb10A2Snorm(Vector4 v) => PackRgb10A2Snorm(v.X, v.Y, v.Z, Snorm10(v.W));
private static uint PackRgb10A2Snorm(Vector4 v) => PackRgb10A2Snorm(v.X, v.Y, v.Z, Snorm2(v.W));
private static uint PackRgb10A2Snorm(Vector3 v, uint texLayer) => PackRgb10A2Snorm(v.X, v.Y, v.Z, texLayer & 0x3u);
private static uint PackRgb10A2Snorm(float x, float y, float z, uint w) => Snorm10(x) | (Snorm10(y) << 10) | (Snorm10(z) << 20) | (w << 30);
private static uint Snorm10(float f) => (uint)(int)MathF.Round(Math.Clamp(f, -1f, 1f) * 511f) & 0x3FFu;
private static uint Snorm2(float f) => f < 0f ? 3u : 1u;
}

public unsafe struct PerMaterialMeshData : IPerMaterialData
Expand Down Expand Up @@ -189,7 +190,7 @@ public Geometry(MeshVertex[] vertices, uint[] indices, FColor[]? colors, FMeshUV
{
var vertex = vertices[i];
var position = new Vector3(vertex.Position.X, vertex.Position.Z, vertex.Position.Y) * Settings.GlobalScale;
var normal = new Vector4(vertex.Normal.X, vertex.Normal.Z, vertex.Normal.Y, vertex.Normal.W);
var normal = new Vector4(vertex.Normal.X, vertex.Normal.Z, vertex.Normal.Y, -vertex.Normal.W);
var tangent = new Vector3(vertex.Tangent.X, vertex.Tangent.Z, vertex.Tangent.Y);
var texCoord = new Vector2(vertex.Uv.U, vertex.Uv.V);
var texLayer = extraUvs != null ? (uint)Math.Floor(extraUvs[i].U) : 0u;
Expand Down Expand Up @@ -219,7 +220,7 @@ public Geometry(SkinnedMeshVertex[] vertices, uint[] indices, FColor[]? colors,
{
var vertex = vertices[i];
var position = new Vector3(vertex.Position.X, vertex.Position.Z, vertex.Position.Y) * Settings.GlobalScale;
var normal = new Vector4(vertex.Normal.X, vertex.Normal.Z, vertex.Normal.Y, vertex.Normal.W);
var normal = new Vector4(vertex.Normal.X, vertex.Normal.Z, vertex.Normal.Y, -vertex.Normal.W);
var tangent = new Vector3(vertex.Tangent.X, vertex.Tangent.Z, vertex.Tangent.Y);
var texCoord = new Vector2(vertex.Uv.U, vertex.Uv.V);
var texLayer = extraUvs != null ? (uint)Math.Floor(extraUvs[i].U) : 0u;
Expand Down
4 changes: 2 additions & 2 deletions Snooper/Rendering/Managers/PostProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public override void Generate()
shader.SetUniform("ssao", 4);
}

if (ctx.LightSystem is { IsEnabled: true } system)
if (ctx.LightSystem is { IsEnabled: true, HasClusters: true } system)
{
system.BindForRendering();
shader.SetUniform("useLighting", true);
Expand Down Expand Up @@ -191,7 +191,7 @@ public override void Generate()
shader.SetUniform("uShowGrid", ctx.ShowGrid);
shader.SetUniform("uMaxLightsPerCluster", ClusteredLightSystem.MaxLightsPerClusterLimit);

if (ctx.LightSystem is { IsEnabled: true } system)
if (ctx.LightSystem is { IsEnabled: true, HasClusters: true } system)
{
system.BindForRendering();
shader.SetUniform("uHasLights", true);
Expand Down
7 changes: 1 addition & 6 deletions Snooper/Rendering/Systems/BillboardSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,5 @@ public class BillboardSystem : PrimitiveSystem<Vector2, BillboardComponent, PerI
[CommandBufferType.Transparent] = new EmbeddedShader("billboard")
};

protected override Action<uint> VertexLayout { get; } = vao =>
{
GL.VertexArrayAttribFormat(vao, 0, 2, VertexAttribType.Float, false, 0);
GL.EnableVertexArrayAttrib(vao, 0);
GL.VertexArrayAttribBinding(vao, 0, 0);
};
protected override Action<VertexArrayLayout> VertexLayout { get; } = layout => layout.Float(0, 2);
}
6 changes: 4 additions & 2 deletions Snooper/Rendering/Systems/ClusteredLightSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ internal abstract class LightBindings : Bindings
public int GridDimensionX { get; private set; }
public int GridDimensionY { get; private set; }
public int GridDimensionZ => 16;
public bool HasClusters => _numClusters > 0;

private int _numClusters;
private int _numWorkGroups;
Expand Down Expand Up @@ -161,7 +162,7 @@ private bool ConsumeClustersDirty(CameraComponent camera)

private void BuildClusters(CameraComponent camera)
{
if (_numClusters == 0) return;
if (!HasClusters) return;

_clusterBuildProgram.Use();
_clusterBuildProgram.SetUniform("uScreenWidth", _screenWidth);
Expand All @@ -183,7 +184,7 @@ private void BuildClusters(CameraComponent camera)

private void CullLights(CameraComponent camera)
{
if (_numClusters == 0 || _lightDataBuffer.Count == 0)
if (!HasClusters || _lightDataBuffer.Count == 0)
{
return;
}
Expand Down Expand Up @@ -255,6 +256,7 @@ public void Resize(int newWidth, int newHeight)
GridDimensionY = (_screenHeight + TileSize - 1) / TileSize;
_numClusters = GridDimensionX * GridDimensionY * GridDimensionZ;
_numWorkGroups = (_numClusters + WorkGroupSize - 1) / WorkGroupSize;
if (!HasClusters) return;

_clusterAABBBuffer.Reallocate(_numClusters);
_clusterDataBuffer.Reallocate(_numClusters);
Expand Down
Loading
Loading