feat(render): implement Campaign AR and terrain fidelity

This commit is contained in:
Erik 2026-08-22 13:13:29 +02:00
parent 99cf26e00c
commit 7a5f96ede5
368 changed files with 50611 additions and 950 deletions

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Numerics;
using AcDream.App.UI;
using AcDream.App.UI.Layout;
using AcDream.Plugin.Abstractions.Rendering;
using AcDream.UI.Abstractions.Panels.Settings;
namespace AcDream.App.Tests.UI.Layout;
@ -285,7 +286,8 @@ public sealed class ConfigOptionsPageControllerTests
public List<CameraTurningSettings> CameraTurningSaves { get; } = new();
public List<ChatSettings> ChatSaves { get; } = new();
public ConfigOptionsPageController.Bindings ToBindings() => new(
public ConfigOptionsPageController.Bindings ToBindings(
ConfigOptionsPageController.RenderPackBindings? renderPacks = null) => new(
LoadDisplay: () => Display,
SaveDisplay: value => { Display = value; DisplaySaves.Add(value); },
LoadAudio: () => Audio,
@ -293,7 +295,10 @@ public sealed class ConfigOptionsPageControllerTests
LoadCameraTurning: () => CameraTurning,
SaveCameraTurning: value => { CameraTurning = value; CameraTurningSaves.Add(value); },
LoadChat: () => Chat,
SaveChat: value => { Chat = value; ChatSaves.Add(value); });
SaveChat: value => { Chat = value; ChatSaves.Add(value); })
{
RenderPacks = renderPacks,
};
}
private static (OptionsPanelController Panel, FakeBindings Bindings, bool Bound) BindReal(
@ -430,6 +435,377 @@ public sealed class ConfigOptionsPageControllerTests
Assert.Equal(39, viewport.Children.Count);
}
[Fact]
public void OptInRenderPackBindings_append_two_menus_without_changing_retail_only_fixture()
{
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
OptionsPanelController controller = OptionsPanelController.Bind(
layout,
new OptionsPanelController.Callbacks(
Toggle: () => { },
RequestExitToCharacterSelection: () => { },
ExitGame: () => { },
UseMouseTurningSettings: () => { },
DisplaySystemMessage: _ => { }))!;
var fake = new FakeBindings();
string? activationFailure = "Last activation failed: shader interface mismatch.";
var renderPacks = new ConfigOptionsPageController.RenderPackBindings(() =>
[
new ConfigOptionsPageController.RenderPackChoice(
"acdream.atmospheric",
"Atmospheric Rendering",
"1.0.0",
true,
null,
[
new ConfigOptionsPageController.RenderPackPresetChoice(
"low", "Low", true, null)
{
MaxResidentGpuBytes = 64L * 1024 * 1024,
MaxIncrementalGpuMillisecondsP50 = 2.0,
MaxIncrementalGpuMillisecondsP99 = 3.0,
MaxIncrementalCpuMillisecondsP50 = 0.15,
MaxIncrementalCpuMillisecondsP99 = 0.50,
},
new("medium", "Medium", true, null),
new("high", "High", false, "High needs more GPU memory."),
])
{
FeatureSummary = "Filmic atmosphere and moving-sun shadows.",
},
new ConfigOptionsPageController.RenderPackChoice(
"test.unsupported",
"Unsupported Test Pack",
"2.0.0",
false,
"Directional depth sampling is unavailable.",
[new("low", "Low", false, "Directional depth sampling is unavailable.")]),
])
{
LoadFailureNotice = () => activationFailure,
};
bool bound = ConfigOptionsPageController.Bind(
layout,
controller.ConfigPage,
MakeTemplateResolver(),
(_, _) => null,
fake.ToBindings(renderPacks));
Assert.True(bound);
var configSlot = UiElement.FindDescendant(controller.TabPanel, ConfigPageSlotId)!;
var listBox = Assert.IsType<UiTemplateListBox>(
UiElement.FindDescendant(configSlot, ConfigOptionsPageController.ListBoxElementId));
Assert.Equal(43, Assert.Single(listBox.Children).Children.Count);
Assert.Equal(32, controller.ConfigPage.Rows.Count);
List<UiMenu> menus = CollectMenus(configSlot);
UiMenu packMenu = menus[^2];
UiMenu presetMenu = menus[^1];
Assert.Equal("acdream default (retail-faithful)", packMenu.Items[0].Label);
Assert.Contains(packMenu.Items, value => Equals(value.Payload, "acdream.atmospheric"));
Assert.False(packMenu.EnabledProvider!("test.unsupported"));
Assert.Equal(
"Last activation failed: shader interface mismatch.",
packMenu.GetTooltipText()!.Split(Environment.NewLine)[0]);
packMenu.OnSelect!("test.unsupported");
Assert.True(fake.Display.RenderPack.IsRetail);
packMenu.OnSelect!("acdream.atmospheric");
Assert.Equal("acdream.atmospheric", fake.Display.RenderPack.PackId);
Assert.Equal("1.0.0", fake.Display.RenderPack.PackVersion);
Assert.Equal("low", fake.Display.RenderPack.PresetId);
activationFailure = null;
Assert.Equal(
"Filmic atmosphere and moving-sun shadows.",
packMenu.GetTooltipText());
presetMenu = CollectMenus(configSlot)[^1];
Assert.Equal(3, presetMenu.Items.Count);
Assert.False(presetMenu.EnabledProvider!("high"));
Assert.Contains(
"GPU p50/p99 ≤ 2/3 ms",
presetMenu.GetTooltipText(),
StringComparison.Ordinal);
Assert.Contains("pack VRAM ≤ 64 MiB", presetMenu.GetTooltipText(), StringComparison.Ordinal);
presetMenu.OnSelect!("medium");
Assert.Equal("medium", fake.Display.RenderPack.PresetId);
Assert.True(fake.DisplaySaves.Count >= 2);
}
[Fact]
public void RenderPackMenu_revision_refresh_removes_withdrawn_schema_and_discovers_reregistration()
{
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
OptionsPanelController controller = OptionsPanelController.Bind(
layout,
new OptionsPanelController.Callbacks(
Toggle: () => { },
RequestExitToCharacterSelection: () => { },
ExitGame: () => { },
UseMouseTurningSettings: () => { },
DisplaySystemMessage: _ => { }))!;
var alpha = new ConfigOptionsPageController.RenderPackChoice(
"pack.alpha", "Alpha", "1.0.0", true, null,
[new("low", "Low", true, null)])
{
Settings =
[
new RenderSettingDeclaration(
"alpha-toggle", "Alpha toggle", RenderSettingKind.Boolean,
"true", null, null, null, []),
],
};
var beta = new ConfigOptionsPageController.RenderPackChoice(
"pack.beta", "Beta", "2.0.0", true, null,
[new("medium", "Medium", true, null)]);
IReadOnlyList<ConfigOptionsPageController.RenderPackChoice> discovered = [alpha];
long revision = 1;
var fake = new FakeBindings
{
Display = DisplaySettings.Default with
{
RenderPack = new RenderPackSelectionSettings(
"pack.alpha", "1.0.0", "low"),
},
};
var renderPacks = new ConfigOptionsPageController.RenderPackBindings(
() => discovered)
{
LoadRevision = () => revision,
};
Assert.True(ConfigOptionsPageController.Bind(
layout,
controller.ConfigPage,
MakeTemplateResolver(),
(_, _) => null,
fake.ToBindings(renderPacks)));
var configSlot = UiElement.FindDescendant(controller.TabPanel, ConfigPageSlotId)!;
var listBox = Assert.IsType<UiTemplateListBox>(
UiElement.FindDescendant(configSlot, ConfigOptionsPageController.ListBoxElementId));
UiMenu packMenu = CollectMenus(configSlot)[^2];
Assert.Equal(44, listBox.ItemCount);
Assert.Contains(packMenu.Items, item => Equals(item.Payload, "pack.alpha"));
discovered = [beta];
revision++;
packMenu.BeforeOpen!();
Assert.DoesNotContain(packMenu.Items, item => Equals(item.Payload, "pack.alpha"));
Assert.Contains(packMenu.Items, item => Equals(item.Payload, "pack.beta"));
Assert.Equal(RenderPackSelectionSettings.RetailPackId, packMenu.Selected);
Assert.Equal(43, listBox.ItemCount);
Assert.Equal(32, controller.ConfigPage.Rows.Count);
controller.ConfigPage.Reset();
Assert.Equal(RenderPackSelectionSettings.RetailPackId, packMenu.Selected);
Assert.DoesNotContain(packMenu.Items, item => Equals(item.Payload, "pack.alpha"));
fake.Display = fake.Display with
{
RenderPack = new RenderPackSelectionSettings(
"pack.beta", "2.0.0", "medium"),
};
revision++;
packMenu.BeforeOpen!();
Assert.Equal("pack.beta", packMenu.Selected);
Assert.Equal(43, listBox.ItemCount);
Assert.Equal("Medium", CollectMenus(configSlot)[^1].Items.Single().Label);
}
[Fact]
public void RenderPackSettings_live_schema_swap_replaces_only_the_optional_tail()
{
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
OptionsPanelController controller = OptionsPanelController.Bind(
layout,
new OptionsPanelController.Callbacks(
Toggle: () => { },
RequestExitToCharacterSelection: () => { },
ExitGame: () => { },
UseMouseTurningSettings: () => { },
DisplaySystemMessage: _ => { }))!;
var fake = new FakeBindings
{
Display = DisplaySettings.Default with
{
RenderPack = new RenderPackSelectionSettings("pack.alpha", "1.0.0", "low"),
},
};
RenderSettingDeclaration[] alphaSettings =
[
new("enabled", "Enabled", RenderSettingKind.Boolean, "false", null, null, null, []),
new("strength", "Strength", RenderSettingKind.Float, "0.5", 0, 1, 0.25, []),
new("samples", "Samples", RenderSettingKind.Integer, "2", 0, 10, 2, []),
new("mode", "Mode", RenderSettingKind.Choice, "low", null, null, null,
["low", "high"]),
];
var alpha = new ConfigOptionsPageController.RenderPackChoice(
"pack.alpha", "Alpha", "1.0.0", true, null,
[
new ConfigOptionsPageController.RenderPackPresetChoice(
"low", "Low", true, null)
{
SettingOverrides =
[
new RenderQualitySettingOverride("strength", "0.75"),
],
},
new ConfigOptionsPageController.RenderPackPresetChoice(
"high", "High", true, null),
])
{
Settings = alphaSettings,
};
var beta = new ConfigOptionsPageController.RenderPackChoice(
"pack.beta", "Beta", "2.0.0", true, null,
[new("default", "Default", true, null)])
{
Settings =
[
new RenderSettingDeclaration(
"beta-enabled", "Beta enabled", RenderSettingKind.Boolean,
"true", null, null, null, []),
],
};
var renderPacks = new ConfigOptionsPageController.RenderPackBindings(() =>
[alpha, beta]);
Assert.True(ConfigOptionsPageController.Bind(
layout,
controller.ConfigPage,
MakeTemplateResolver(),
(_, _) => null,
fake.ToBindings(renderPacks)));
var configSlot = UiElement.FindDescendant(controller.TabPanel, ConfigPageSlotId)!;
var listBox = Assert.IsType<UiTemplateListBox>(
UiElement.FindDescendant(configSlot, ConfigOptionsPageController.ListBoxElementId));
Assert.Equal(47, listBox.ItemCount); // exact retail 39 + 8-row optional tail
Assert.Equal(36, controller.ConfigPage.Rows.Count); // retail 30 + pack/preset + 4 settings
IReadOnlyList<UiElement> items = listBox.ViewportForTest!.Children;
var enabled = Assert.IsType<UiButton>(
UiElement.FindDescendant(items[42], 0x10000219u));
var strength = Assert.IsType<UiScrollbar>(
UiElement.FindDescendant(items[43], 0x1000021Cu));
var samples = Assert.IsType<UiScrollbar>(
UiElement.FindDescendant(items[44], 0x1000021Cu));
List<UiMenu> menus = CollectMenus(configSlot);
UiMenu packMenu = menus[^3];
UiMenu presetMenu = menus[^2];
UiMenu oldModeMenu = menus[^1];
Assert.Equal(0.75f, strength.ScalarPosition, 3); // preset wins declaration default
Assert.Equal(["low", "high"], oldModeMenu.Items.Select(value => value.Label));
enabled.Selected = true;
enabled.OnClick!();
strength.ScalarChanged!(0.62f); // 0.62 snaps to 0.5 on the declared 0.25 step
samples.ScalarChanged!(0.33f); // 3.3 snaps to integer step 4
oldModeMenu.OnSelect!("high");
Assert.Equal("true", fake.Display.RenderPack.SettingOverrides["enabled"]);
Assert.Equal("0.5", fake.Display.RenderPack.SettingOverrides["strength"]);
Assert.Equal("4", fake.Display.RenderPack.SettingOverrides["samples"]);
Assert.Equal("high", fake.Display.RenderPack.SettingOverrides["mode"]);
presetMenu.OnSelect!("high");
Assert.Equal("high", fake.Display.RenderPack.PresetId);
Assert.Equal(4, fake.Display.RenderPack.SettingOverrides.Count);
Assert.Equal(47, listBox.ItemCount);
Assert.Equal(36, controller.ConfigPage.Rows.Count);
int savesBeforeStaleWidget = fake.DisplaySaves.Count;
oldModeMenu.OnSelect!("low");
Assert.Equal(savesBeforeStaleWidget, fake.DisplaySaves.Count);
packMenu.OnSelect!("pack.beta");
Assert.Equal("pack.beta", fake.Display.RenderPack.PackId);
Assert.Equal("2.0.0", fake.Display.RenderPack.PackVersion);
Assert.Equal("default", fake.Display.RenderPack.PresetId);
Assert.Empty(fake.Display.RenderPack.SettingOverrides);
Assert.Equal(44, listBox.ItemCount); // header + pack + preset + one setting + separator
Assert.Equal(33, controller.ConfigPage.Rows.Count);
oldModeMenu.OnSelect!("high");
Assert.Empty(fake.Display.RenderPack.SettingOverrides);
controller.ConfigPage.Reset();
Assert.Equal("pack.alpha", fake.Display.RenderPack.PackId);
Assert.Equal(47, listBox.ItemCount);
Assert.Equal(36, controller.ConfigPage.Rows.Count);
controller.ConfigPage.Defaults();
Assert.True(fake.Display.RenderPack.IsRetail);
Assert.Equal(43, listBox.ItemCount); // header + pack + retail preset + separator
Assert.Equal(32, controller.ConfigPage.Rows.Count);
}
[Fact]
public void RenderPackSettingEdit_sanitizes_unknown_and_invalid_persisted_values()
{
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
OptionsPanelController controller = OptionsPanelController.Bind(
layout,
new OptionsPanelController.Callbacks(
Toggle: () => { },
RequestExitToCharacterSelection: () => { },
ExitGame: () => { },
UseMouseTurningSettings: () => { },
DisplaySystemMessage: _ => { }))!;
var fake = new FakeBindings
{
Display = DisplaySettings.Default with
{
RenderPack = new RenderPackSelectionSettings("pack.alpha", "1.0.0", "low")
{
SettingOverrides = new RenderPackSettingOverrides(
new Dictionary<string, string>
{
["removed"] = "1",
["strength"] = "0.6", // not aligned to 0.25
}),
},
},
};
var pack = new ConfigOptionsPageController.RenderPackChoice(
"pack.alpha", "Alpha", "1.0.0", true, null,
[new("low", "Low", true, null)])
{
Settings =
[
new RenderSettingDeclaration(
"enabled", "Enabled", RenderSettingKind.Boolean,
"false", null, null, null, []),
new RenderSettingDeclaration(
"strength", "Strength", RenderSettingKind.Float,
"0.5", 0, 1, 0.25, []),
],
};
Assert.True(ConfigOptionsPageController.Bind(
layout,
controller.ConfigPage,
MakeTemplateResolver(),
(_, _) => null,
fake.ToBindings(new ConfigOptionsPageController.RenderPackBindings(() => [pack]))));
var configSlot = UiElement.FindDescendant(controller.TabPanel, ConfigPageSlotId)!;
var listBox = Assert.IsType<UiTemplateListBox>(
UiElement.FindDescendant(configSlot, ConfigOptionsPageController.ListBoxElementId));
UiElement enabledRow = listBox.ViewportForTest!.Children[42];
var enabled = Assert.IsType<UiButton>(
UiElement.FindDescendant(enabledRow, 0x10000219u));
enabled.Selected = true;
enabled.OnClick!();
Assert.Single(fake.Display.RenderPack.SettingOverrides);
Assert.Equal("true", fake.Display.RenderPack.SettingOverrides["enabled"]);
Assert.False(fake.Display.RenderPack.SettingOverrides.ContainsKey("removed"));
Assert.False(fake.Display.RenderPack.SettingOverrides.ContainsKey("strength"));
}
[Fact]
public void ToggleRow_SfxEnabled_WritesThroughAudioBindings()
{
@ -1043,7 +1419,7 @@ public sealed class ConfigOptionsPageControllerTests
(24, RowKind.Menu, true, "Environment Texture Detail"), // AP-198
(25, RowKind.Menu, true, "Texture Filtering"), // AP-198
(26, RowKind.Menu, true, "Landscape Draw Distance"), // AP-198
(27, RowKind.Toggle, true, "Building Detail Textures"), // AP-198
(27, RowKind.Toggle, false, "Building Detail Textures"), // LIVE — #226
(28, RowKind.Toggle, true, "Multi-Pass Alpha"), // AP-198
(31, RowKind.Slider, true, "Mouse Look Sensitivity"), // TS-74
(32, RowKind.Toggle, true, "Invert Mouselook Y Axis"), // TS-74