using System.Runtime.InteropServices;
namespace AcDream.App.Rendering.Gpu;
///
/// A slice of the current frame's upload ring: the buffer to bind, the byte
/// offset to bind it at, and CPU-writable memory to fill.
///
/// This type replaces every per-frame BufferSubData in the renderers, and
/// it is the reason the Vulkan backend costs less CPU than GL. Today a renderer
/// writes its instance/batch/indirect data into a managed array and then hands
/// that array to the driver, which validates it, copies it, and tracks a renamed
/// backing store. With a ring allocation the renderer writes ONCE, directly into
/// memory the GPU will read — the upload stops existing as a separate step.
///
/// It is a ref struct on purpose: the memory is only valid until the frame
/// that produced it retires, so the compiler prevents storing it in a field.
///
internal readonly ref struct GpuRingAllocation
{
public GpuRingAllocation(IGpuBuffer buffer, uint offsetBytes, Span data)
{
Buffer = buffer;
OffsetBytes = offsetBytes;
Data = data;
}
/// The ring buffer to bind. Backends may hand out many allocations from one buffer.
public IGpuBuffer Buffer { get; }
/// Byte offset of this allocation, already aligned for its .
public uint OffsetBytes { get; }
/// CPU-writable memory for this allocation. Valid until the owning frame retires.
public Span Data { get; }
public bool IsEmpty => Data.IsEmpty;
/// Reinterprets the allocation as a typed span so callers write structs, not bytes.
public Span AsSpan() where T : unmanaged => MemoryMarshal.Cast(Data);
}
///
/// One frame's recording context. Obtained from
/// and closed by , which submits the recorded work and presents.
///
/// The frame owns the ring: allocations are recycled once the GPU has finished the
/// frame that made them, which is exactly the bound
/// GpuFrameFlightController already enforces with GL fences and which the
/// Vulkan backend expresses with a single timeline semaphore.
///
internal interface IGpuFrame : IDisposable
{
/// Frames-in-flight slot index this frame occupies.
int SlotIndex { get; }
/// Monotonic frame serial. Matches the retirement-ledger key used for resource release.
long Serial { get; }
///
/// Reserves bytes of CPU-writable ring memory,
/// aligned as requires. Throws if the request exceeds
/// the ring's per-frame capacity — silently truncating a draw's data would
/// corrupt the frame invisibly.
///
GpuRingAllocation AllocateRing(int byteCount, GpuRingUsage usage);
///
/// Opens a rendering pass. The returned encoder must be disposed before the
/// next pass begins; nesting is not supported and no acdream pass needs it.
///
IGpuPassEncoder BeginPass(GpuPassDescription description);
///
/// Submits the frame and presents it. Idempotent with
/// so a failed frame still closes its slot rather than stalling the ring.
///
void End();
}