using System;
using AcDream.App.Rendering.Gpu;
using AcDream.Core.Lighting;
namespace AcDream.App.Rendering;
///
/// Publishes the SceneLighting UBO each frame so every shader sampling
/// uLights[] / uFogColor / etc reads consistent data without
/// per-shader re-upload.
///
/// The raw-GL arm this used to have alongside it โ a global uniform
/// binding point kept live by a per-flight-slot buffer pool โ was deleted at
/// Campaign V slice V11. Vulkan has no such global binding point: a
/// descriptor set is bound per draw, so the upload is a frame ring slice
/// PUBLISHED on , and each world
/// renderer binds it inside the pass after its own binds (plan ยง5.5.14 item
/// 2). Every allocation within a frame is already distinct memory that lives
/// until the frame retires, which is the property the deleted buffer pool
/// existed to provide when the world, portal-space and paperdoll draws each
/// upload different lighting.
///
public sealed unsafe class SceneLightingUboBinding : IDisposable
{
private readonly ICurrentGpuFrameSource _frames;
private readonly WorldFrameSections _sections;
private bool _frameStarted;
///
/// The GL arm's per-flight-slot buffer pool this used to report on was
/// deleted at Campaign V slice V11: every allocation now comes from a ring
/// that lives until its frame retires, so there is no persistent
/// dynamic-buffer pool left to size.
///
internal int DynamicBufferCount => 0;
internal SceneLightingUboBinding(
ICurrentGpuFrameSource frames,
WorldFrameSections sections)
{
_frames = frames ?? throw new ArgumentNullException(nameof(frames));
_sections = sections ?? throw new ArgumentNullException(nameof(sections));
}
///
/// Resets the lighting-submission cursor for a GPU-fenced frame slot.
/// World, portal-space, and paperdoll draws can each upload different
/// lighting in one frame, so every upload receives distinct storage.
///
public void BeginFrame(int frameSlot)
{
ArgumentOutOfRangeException.ThrowIfNegative(frameSlot);
_frameStarted = true;
}
///
/// Push the current frame's UBO contents to the GPU. Cheap (576 bytes)
/// so fine to call unconditionally every frame.
///
public void Upload(SceneLightingUbo data)
{
if (!_frameStarted)
throw new InvalidOperationException("BeginFrame must be called before uploading scene lighting.");
IGpuFrame frame = _frames.CurrentFrame
?? throw new InvalidOperationException(
"Scene lighting requires an open IGpuFrame (see GpuDeviceFrameLifetime).");
GpuRingAllocation allocation = frame.AllocateRing(
SceneLightingUbo.SizeInBytes,
GpuRingUsage.Uniform);
new ReadOnlySpan(&data, SceneLightingUbo.SizeInBytes)
.CopyTo(allocation.Data);
_sections.SceneLighting = new GpuBufferSection(
allocation.Buffer,
allocation.OffsetBytes,
(uint)SceneLightingUbo.SizeInBytes);
}
///
/// No-op: this binding owns no GPU resource of its own on the RHI arm โ the
/// deleted GL arm's per-flight buffer pool was the only thing to release.
/// Kept as a method so callers that hold this behind an
/// reference (composition's acquisition scope) don't need a special case.
///
public void Dispose()
{
}
}