namespace AcDream.App.Rendering.Packs;
///
/// The one rule for a pack preset's effective resident-GPU ceiling.
///
///
///
/// A preset's MaxResidentGpuBytes is the 1080p figure of the
/// Campaign AR budget table (Low 64 / Medium 128 / High 256 MiB). The pack's
/// resident set is dominated by screen-sized images — the HDR world colour +
/// depth targets alone are 12 bytes per pixel, plus the ray/bloom
/// intermediates — so a ceiling that does not scale with pixel count refuses
/// the pack the moment the user picks a larger mode: at 2560×1440 the Low
/// preset's screen targets are 44 MB of a 64 MiB budget, and live Holtburg's
/// scene-dependent shadow command buffers pushed the total to 64.25 MiB.
/// That refusal is what the owner hit at the Campaign VM VM7 gate
/// (2026-08-23, #425): Apply → "needs 67368164 resident GPU bytes; the
/// active pack budget is 67108864" → persisted back to the default path.
///
///
/// The effective ceiling is therefore the declared 1080p figure scaled by
/// the pixel-count ratio (never below 1, so smaller modes keep the declared
/// ceiling), and still capped by the hardware's MaxPackResidentBytes.
/// The parts of the resident set that do NOT scale with the screen (shadow
/// depth maps, shadow command/transform buffers) are over-allowed by the same
/// factor; that errs on the side of honouring the user's explicit resolution
/// choice while keeping the bound proportional to what they asked the GPU to
/// draw. tools/run-atmospheric-performance-matrix.ps1 judges its
/// resident column with this same rule so the tool and the runtime cannot
/// disagree.
///
///
internal static class RenderPackResidentBudget
{
internal const long ReferencePixels = 1920L * 1080L;
internal static long Effective(
long declaredBytesAt1080p,
int viewportWidth,
int viewportHeight,
long hardwareCapBytes)
{
ArgumentOutOfRangeException.ThrowIfNegative(declaredBytesAt1080p);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(viewportWidth);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(viewportHeight);
ArgumentOutOfRangeException.ThrowIfNegative(hardwareCapBytes);
long pixels = checked((long)viewportWidth * viewportHeight);
long scaled = pixels <= ReferencePixels
? declaredBytesAt1080p
: checked((long)Math.Ceiling(
(double)declaredBytesAt1080p * pixels / ReferencePixels));
return Math.Min(scaled, hardwareCapBytes);
}
}