namespace AcDream.App.Rendering;
///
/// Campaign V slice V4a bridge: memoizes a for the
/// most recent raw GL colour-texture name a still-unmigrated private-viewport
/// renderer (,
/// ) produced, so
/// UiViewport.TextureHandle — now a — has
/// something to draw. Those renderers still allocate their FBO colour
/// attachment directly on GL (V4g's scope: "PrivateEntityViewportRenderer
/// → IGpuRenderTarget"), so this bridge — not their own campaign slice — is
/// what lets the RETAINED UI side of the seam move onto the RHI now.
///
/// Registers lazily and only re-registers when the producer hands back a
/// DIFFERENT raw name (the FBO's colour texture is stable across frames at a
/// fixed viewport size and only regenerates on resize) — a naive
/// register-every-frame would exhaust the 16384-slot table in seconds.
/// Deleted at slice V4g once those renderers publish a real
/// directly.
///
internal sealed class ExternalViewportTextureBridge
{
private readonly IGpuDevice _device;
private readonly IGpuSampler _sampler;
private uint _lastRawName;
private GpuTextureSlot _lastSlot = GpuTextureSlot.Unassigned;
public ExternalViewportTextureBridge(IGpuDevice device)
{
_device = device ?? throw new ArgumentNullException(nameof(device));
// Matches the FBO colour attachment's own fixed GL_LINEAR / CLAMP_TO_EDGE
// parameters (PrivateEntityViewportRenderer.EnsureFramebuffer,
// PaperdollViewportRenderer's equivalent): a bindless handle's
// filtering comes from the bound SAMPLER object, not the texture's
// own — now irrelevant — TEXTURE_MIN_FILTER/TEXTURE_MAG_FILTER.
_sampler = device.CreateSampler(GpuSamplerDescription.WorldClamp);
}
///
/// Resolves (0 = nothing rendered
/// this call, matching the producers' existing "0 = no texture" return)
/// to a texture-table slot, registering it once per distinct name.
///
public GpuTextureSlot Resolve(uint rawGlColorTextureName)
{
if (rawGlColorTextureName == 0)
{
Release();
return GpuTextureSlot.Unassigned;
}
if (rawGlColorTextureName == _lastRawName && _lastSlot.IsAssigned)
return _lastSlot;
Release();
if (_device is not Gpu.Gl.GlGpuDevice glDevice)
{
throw new NotSupportedException(
"ExternalViewportTextureBridge only supports the GL backend. " +
"Slice V4g removes this bridge before any other backend ships.");
}
_lastSlot = glDevice.RegisterExternalColorTexture(rawGlColorTextureName, _sampler);
_lastRawName = rawGlColorTextureName;
return _lastSlot;
}
private void Release()
{
if (_lastSlot.IsAssigned)
_device.ReleaseTextureSlot(_lastSlot);
_lastSlot = GpuTextureSlot.Unassigned;
_lastRawName = 0;
}
}