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(); }