feat(A.5 T22.5): QualityPreset schema + tests (commit 1/2)

Add QualityPreset enum + QualitySettings readonly record struct with
From(preset) table and WithEnvOverrides() env-var override layer.
Four presets (Low/Medium/High/Ultra) drive NearRadius, FarRadius,
MsaaSamples, AnisotropicLevel, AlphaToCoverage, MaxCompletionsPerFrame.
Env vars (ACDREAM_NEAR_RADIUS, ACDREAM_FAR_RADIUS, ACDREAM_MSAA_SAMPLES,
ACDREAM_ANISOTROPIC, ACDREAM_A2C, ACDREAM_MAX_COMPLETIONS_PER_FRAME)
override individual preset fields for dev spot-testing.

DisplaySettings gains a Quality: QualityPreset field (default High);
SettingsStore persists/loads it under display."quality" as an enum
name string with Enum.TryParse fallback. 12 new QualityPresetTests
cover the preset table (radii, msaa, aniso, a2c, completions) and all
six env-var override paths. 415 UI.Abstractions tests passing.

Wiring into GameWindow / WbDrawDispatcher / TerrainAtlas follows in
commit 2 of this task.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-10 08:37:17 +02:00
parent c473feedb3
commit afa4200107
5 changed files with 272 additions and 9 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;
using AcDream.UI.Abstractions.Settings;
namespace AcDream.UI.Abstractions.Panels.Settings;
@ -62,12 +63,13 @@ public sealed class SettingsStore
var d = DisplaySettings.Default;
return new DisplaySettings(
Resolution: ReadString (disp, "resolution", d.Resolution),
Fullscreen: ReadBool (disp, "fullscreen", d.Fullscreen),
VSync: ReadBool (disp, "vsync", d.VSync),
FieldOfView: ReadFloat (disp, "fieldOfView", d.FieldOfView),
Gamma: ReadFloat (disp, "gamma", d.Gamma),
ShowFps: ReadBool (disp, "showFps", d.ShowFps));
Resolution: ReadString (disp, "resolution", d.Resolution),
Fullscreen: ReadBool (disp, "fullscreen", d.Fullscreen),
VSync: ReadBool (disp, "vsync", d.VSync),
FieldOfView: ReadFloat (disp, "fieldOfView", d.FieldOfView),
Gamma: ReadFloat (disp, "gamma", d.Gamma),
ShowFps: ReadBool (disp, "showFps", d.ShowFps),
Quality: ReadQuality (disp, "quality", d.Quality));
}
catch (Exception ex)
{
@ -327,6 +329,7 @@ public sealed class SettingsStore
["fieldOfView"] = d.FieldOfView,
["fullscreen"] = d.Fullscreen,
["gamma"] = d.Gamma,
["quality"] = d.Quality.ToString(),
["resolution"] = d.Resolution,
["showFps"] = d.ShowFps,
["vsync"] = d.VSync,
@ -405,4 +408,12 @@ public sealed class SettingsStore
private static float ReadFloat(JsonElement obj, string name, float fallback)
=> obj.TryGetProperty(name, out var el) && el.ValueKind == JsonValueKind.Number
? el.GetSingle() : fallback;
private static QualityPreset ReadQuality(JsonElement obj, string name, QualityPreset fallback)
{
if (!obj.TryGetProperty(name, out var el) || el.ValueKind != JsonValueKind.String)
return fallback;
var s = el.GetString();
return Enum.TryParse<QualityPreset>(s, ignoreCase: true, out var v) ? v : fallback;
}
}