fix #425: pack resident budget is a 1080p ceiling that scales with pixel count; an explicit Apply retries a failed selection

Live Holtburg at 2560x1440: the Low preset needed 67,368,164 resident bytes
(screen-sized HDR/depth/ray targets are 44 MB of that) against an absolute
64 MiB ceiling that had only been validated at 1080p, so Options -> Apply
fell back to the default path; every later Apply was then refused by the
controller's failure memo, which treated the user's deliberate choice like
automatic re-activation.

RenderPackResidentBudget.Effective scales the declared 1080p figure by the
viewport's pixel-count ratio (never below 1), still capped by the hardware
MaxPackResidentBytes; both pack graphs use it and the performance-matrix
tool judges its resident column by the same rule (contract test updated).
RenderPackController.Request gains explicitUserChoice, which clears the
memo for that selection; RenderPackSelectionBinding passes it on every
display edge (Apply, including resolution changes) and keeps the memo for
the startup request.

Tests: RenderPackResidentBudgetTests (1080p/720p keep the declared
ceiling, 1440p = 16/9x, 4K = 4x, hardware cap wins, zero extent rejected);
controller explicit-retry; the binding test now proves the user's next
Apply activates once the cause is gone. App hermetic lane 6,068/0 (Release).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-23 10:00:11 +02:00
parent 38def07edb
commit 132395e6f7
11 changed files with 250 additions and 8 deletions

View file

@ -291,6 +291,47 @@ public sealed class RenderPackControllerTests
Assert.Equal(1, assets.OpenCount);
}
[Fact]
public void An_explicit_user_request_retries_a_selection_that_failed_earlier()
{
// #425 (Campaign VM VM7 owner gate): a preset that failed its resident
// budget once locked the user out of the pack until restart, because
// the Options panel's Apply went through the same memo as automatic
// re-activation. An explicit choice is a fresh attempt — here the
// second attempt opens the assets again and fails on its own merits
// (still invalid SPIR-V), not with the "will not be retried" memo.
RenderPackDescriptor descriptor = Descriptor() with
{
Passes =
[
new RenderPassDeclaration(
"tone-map",
RenderPassHook.ToneMap,
"shaders/fullscreen.vert.spv",
"shaders/tone-map.frag.spv",
[RenderSemanticInput.WorldColor],
[],
[]),
],
};
var assets = new StubAssets([1, 2, 3, 4]);
using var registry = new BufferedRenderPackRegistry();
using IDisposable registration = registry.Register(descriptor, assets);
var factory = new StubFactory();
using var controller = Controller(registry, factory);
controller.Request(Selection());
RenderPackActivationSnapshot first = controller.ApplyAtFrameBoundary(Extent);
controller.Request(Selection(), explicitUserChoice: true);
RenderPackActivationSnapshot second = controller.ApplyAtFrameBoundary(Extent);
Assert.Equal(RenderPackActivationState.FailedToRetail, first.State);
Assert.Equal(RenderPackActivationState.FailedToRetail, second.State);
Assert.Contains("not valid SPIR-V", second.Reason, StringComparison.Ordinal);
Assert.DoesNotContain("will not be retried", second.Reason, StringComparison.Ordinal);
Assert.Equal(2, assets.OpenCount);
}
[Fact]
public void Arbitrary_plugin_asset_exception_is_contained_as_a_retail_fallback()
{
@ -682,6 +723,20 @@ public sealed class RenderPackControllerTests
Assert.True(settings.Display.RenderPack.IsRetail);
Assert.Contains("pipeline rejected", controller.Snapshot.Reason, StringComparison.Ordinal);
Assert.Null(controller.ActiveRuntime);
// #425: the user's next Apply of the SAME selection is an explicit
// choice — with the failure cause gone it activates instead of being
// refused with the "will not be retried" memo.
factory.Failure = null;
settings.SaveDisplay(settings.Display with
{
RenderPack = Selection() with { PresetId = "medium" },
});
RenderPackActivationSnapshot retried = binding.ApplyAtFrameBoundary(Extent);
Assert.NotEqual(RenderPackActivationState.FailedToRetail, retried.State);
Assert.DoesNotContain("will not be retried", retried.Reason ?? string.Empty, StringComparison.Ordinal);
Assert.NotNull(controller.ActiveRuntime);
}
[Fact]