using System;
using System.Runtime.InteropServices;
using AcDream.Core.Lighting;
using Silk.NET.OpenGL;
namespace AcDream.App.Rendering;
///
/// GL wrapper that owns the SceneLighting UBO buffer, updates its
/// contents each frame, and keeps it bound at binding=1 so every
/// shader sampling uLights[] / uFogColor / etc reads
/// consistent data without per-shader re-upload.
///
///
/// Usage (r12 §13.2 + r13 §12.3):
///
/// - Instantiate once at startup, after the GL context exists.
/// - Each frame, after , call with a freshly-built .
/// - Shader programs that declare layout(std140, binding = 1) uniform SceneLighting { ... } automatically pick up the data.
///
///
///
public sealed unsafe class SceneLightingUboBinding : IDisposable
{
private readonly GL _gl;
private readonly uint _ubo;
private bool _disposed;
public SceneLightingUboBinding(GL gl)
{
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
_ubo = _gl.GenBuffer();
_gl.BindBuffer(BufferTargetARB.UniformBuffer, _ubo);
// Pre-allocate with the final size; BufferSubData each frame.
_gl.BufferData(
BufferTargetARB.UniformBuffer,
(nuint)SceneLightingUbo.SizeInBytes,
(void*)0,
BufferUsageARB.DynamicDraw);
_gl.BindBuffer(BufferTargetARB.UniformBuffer, 0);
// Bind the buffer to the chosen binding point exactly once — shaders
// that declare this binding in their layout block will read from it
// on every draw without further intervention.
_gl.BindBufferBase(BufferTargetARB.UniformBuffer,
SceneLightingUbo.BindingPoint, _ubo);
}
///
/// 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)
{
_gl.BindBuffer(BufferTargetARB.UniformBuffer, _ubo);
_gl.BufferSubData(BufferTargetARB.UniformBuffer,
(nint)0, (nuint)SceneLightingUbo.SizeInBytes, &data);
_gl.BindBuffer(BufferTargetARB.UniformBuffer, 0);
}
public void Dispose()
{
if (_disposed) return;
_gl.DeleteBuffer(_ubo);
_disposed = true;
}
}