146 lines
4.7 KiB
C#
146 lines
4.7 KiB
C#
using System.Runtime.CompilerServices;
|
|
using AcDream.App.Rendering.Wb;
|
|
using AcDream.App.Rendering.Gpu;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
internal readonly record struct DirectionalShadowTerrainRange(
|
|
uint FirstIndex,
|
|
int IndexCount);
|
|
|
|
internal readonly record struct DirectionalShadowTerrainGeometry(
|
|
IGpuBuffer VertexBuffer,
|
|
IGpuBuffer IndexBuffer);
|
|
|
|
/// <summary>
|
|
/// The complete resident terrain arena expressed once as indirect commands.
|
|
/// It deliberately has no camera, PView, portal, or cascade input.
|
|
/// </summary>
|
|
internal sealed class DirectionalShadowTerrainPreparedDraws
|
|
{
|
|
private DrawElementsIndirectCommand[] _commands = [];
|
|
private int _count;
|
|
private bool _building;
|
|
|
|
public long SourceFrameSequence { get; private set; }
|
|
|
|
public ulong BuildSequence { get; private set; }
|
|
|
|
public ReadOnlySpan<DrawElementsIndirectCommand> Commands =>
|
|
_commands.AsSpan(0, _count);
|
|
|
|
public long RetainedScratchBytes =>
|
|
checked((long)_commands.Length
|
|
* Unsafe.SizeOf<DrawElementsIndirectCommand>());
|
|
|
|
public bool TryBegin(long frameSequence, int estimatedCommands)
|
|
{
|
|
if (_building)
|
|
throw new InvalidOperationException(
|
|
"A terrain shadow draw build is already active.");
|
|
if (frameSequence <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(frameSequence));
|
|
ArgumentOutOfRangeException.ThrowIfNegative(estimatedCommands);
|
|
if (SourceFrameSequence == frameSequence)
|
|
return false;
|
|
|
|
EnsureCapacity(estimatedCommands);
|
|
_count = 0;
|
|
_building = true;
|
|
return true;
|
|
}
|
|
|
|
public void Add(in DirectionalShadowTerrainRange range)
|
|
{
|
|
if (!_building)
|
|
throw new InvalidOperationException(
|
|
"Begin a terrain shadow draw build before adding ranges.");
|
|
if (range.IndexCount <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(range));
|
|
EnsureCapacity(checked(_count + 1));
|
|
_commands[_count++] = new DrawElementsIndirectCommand
|
|
{
|
|
Count = checked((uint)range.IndexCount),
|
|
InstanceCount = 1,
|
|
FirstIndex = range.FirstIndex,
|
|
BaseVertex = 0,
|
|
BaseInstance = 0,
|
|
};
|
|
}
|
|
|
|
public void Complete(long frameSequence)
|
|
{
|
|
if (!_building)
|
|
throw new InvalidOperationException(
|
|
"No terrain shadow draw build is active.");
|
|
if (frameSequence <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(frameSequence));
|
|
SourceFrameSequence = frameSequence;
|
|
BuildSequence = checked(BuildSequence + 1);
|
|
_building = false;
|
|
}
|
|
|
|
public void Abort()
|
|
{
|
|
_count = 0;
|
|
_building = false;
|
|
}
|
|
|
|
private void EnsureCapacity(int required)
|
|
{
|
|
if (_commands.Length >= required)
|
|
return;
|
|
int capacity = _commands.Length == 0 ? 16 : _commands.Length;
|
|
while (capacity < required)
|
|
capacity = checked(capacity * 2);
|
|
Array.Resize(ref _commands, capacity);
|
|
}
|
|
}
|
|
|
|
public sealed partial class TerrainModernRenderer
|
|
{
|
|
private readonly DirectionalShadowTerrainPreparedDraws
|
|
_directionalShadowTerrainDraws = new();
|
|
private long _directionalShadowFrameSequence;
|
|
|
|
internal DirectionalShadowTerrainGeometry GetDirectionalShadowGeometry() => new(
|
|
_vertexStore ?? throw new InvalidOperationException("Terrain has no vertex store."),
|
|
_indexStore ?? throw new InvalidOperationException("Terrain has no index store."));
|
|
|
|
/// <summary>
|
|
/// Builds one all-resident indirect list for the current frame. Repeated
|
|
/// calls by individual cascades return the same retained product.
|
|
/// </summary>
|
|
internal DirectionalShadowTerrainPreparedDraws
|
|
PrepareDirectionalShadowDraws()
|
|
{
|
|
if (!_directionalShadowTerrainDraws.TryBegin(
|
|
_directionalShadowFrameSequence,
|
|
_alloc.LoadedCount))
|
|
{
|
|
return _directionalShadowTerrainDraws;
|
|
}
|
|
|
|
try
|
|
{
|
|
for (int slot = 0; slot < _slots.Length; slot++)
|
|
{
|
|
SlotData? data = _slots[slot];
|
|
if (data is null)
|
|
continue;
|
|
var range = new DirectionalShadowTerrainRange(
|
|
data.FirstIndex,
|
|
data.IndexCount);
|
|
_directionalShadowTerrainDraws.Add(in range);
|
|
}
|
|
_directionalShadowTerrainDraws.Complete(
|
|
_directionalShadowFrameSequence);
|
|
return _directionalShadowTerrainDraws;
|
|
}
|
|
catch
|
|
{
|
|
_directionalShadowTerrainDraws.Abort();
|
|
throw;
|
|
}
|
|
}
|
|
}
|