Files
sbox-public/engine/Sandbox.Engine/Scene/Components/IndirectLighting/DDGIVolumeSystem.cs
Lorenz Junglas eb24ff580a Fix DDGI stale buffer crash (#4313)
* Clear DDGI buffer, so we dont pass garbage to the driver later and crash

* Poison vertex buffer memory so we can detect use after free more easily

Should have 0 overhead in release builds
2026-03-16 18:50:11 +01:00

93 lines
2.2 KiB
C#

namespace Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Maintains GPU data for all <see cref="IndirectLightVolume"/> instances in a scene.
/// Collects, sorts, and uploads volume parameters to the renderer.
/// </summary>
sealed class DDGIVolumeSystem : GameObjectSystem<DDGIVolumeSystem>
{
private GpuBuffer<IndirectLightVolume.DDGIVolumeGpuData> GpuBuffer;
private bool _dirty = true;
public DDGIVolumeSystem( Scene scene ) : base( scene )
{
Listen( Stage.FinishUpdate, 0, UpdateVolumes, "UpdateDDGIVolumes" );
}
public override void Dispose()
{
ReleaseBuffer();
Scene?.RenderAttributes?.Set( "DDGI_VolumeCount", 0 );
Scene?.RenderAttributes?.Set( "DDGI_Volumes", (GpuBuffer)null );
base.Dispose();
}
internal void MarkDirty()
{
_dirty = true;
}
private void UpdateVolumes()
{
using var _ = PerformanceStats.Timings.Render.Scope();
if ( Application.IsHeadless || !_dirty )
return;
if ( Scene?.RenderAttributes is null )
return;
_dirty = false;
var orderedVolumes = Scene
.GetAll<IndirectLightVolume>()
.Where( volume => volume is { Active: true } )
.OrderBy( volume => volume.Bounds.Volume );
var volumeData = new List<IndirectLightVolume.DDGIVolumeGpuData>();
foreach ( var volume in orderedVolumes )
{
if ( volume.Enabled && volume.BuildData( out var data ) )
{
volumeData.Add( data );
}
}
if ( volumeData.Count > 0 )
{
EnsureBufferCapacity( volumeData.Count );
GpuBuffer.SetData( volumeData );
Scene.RenderAttributes.Set( "DDGI_VolumeCount", volumeData.Count );
Scene.RenderAttributes.Set( "DDGI_Volumes", GpuBuffer );
return;
}
// No valid volumes: clear renderer attributes to avoid stale data.
ReleaseBuffer();
Scene.RenderAttributes.Set( "DDGI_VolumeCount", 0 );
Scene.RenderAttributes.Set( "DDGI_Volumes", (GpuBuffer)null );
}
private void EnsureBufferCapacity( int count )
{
if ( GpuBuffer is not null && GpuBuffer.ElementCount >= count )
return;
ReleaseBuffer();
GpuBuffer = new GpuBuffer<IndirectLightVolume.DDGIVolumeGpuData>( Math.Max( count, 1 ), debugName: "DDGI_Volumes" );
}
private void ReleaseBuffer()
{
GpuBuffer?.Dispose();
GpuBuffer = null;
}
}