Merge feat/opengl-vulkan: settings for the OpenGL 4.6 and Vulkan 1.4 simulator

A transitional variant, since the exe a player runs may not have the new
renderers. The old renderers stay in the list and the old keys are still written
next to the new ones. A new exe without Vulkan starts OpenGL by itself, an old one
reads the word as its legacy renderer, and both pass over the keys they do not know.

Taken from Hirek193's branch:
- the new keys as rows of a section of their own, with their texts
- their values on the graphics levels, in steps over the six levels here, with
  High as what the simulator does with nothing set
- Vulkan 1.4 in the list of renderers, and its own keys marked as idle without it
- gfx.buffer.percent next to the old buffer keys, read before them
- gfx.antialiasing saved below multisampling, for the later line to count

Keys checked against feat/gl46-migration of the simulator.

Left out, as this branch has its own or does not need them: the rewritten settings
page and its five levels, GraphicsRules, the DLSS library check, eu07.ini import and
export, and the removal of the old renderers.
This commit is contained in:
maj00r
2026-11-01 19:43:46 +01:00
20 changed files with 913 additions and 25 deletions

View File

@@ -17,6 +17,31 @@ public class ConfigFileTests
Assert.Equal($"a 1{nl}b 2{nl}", ConfigFile.Parse("a 1\r\nb 2\r\n").ToText());
}
[Fact]
public void A_line_above_another_moves_right_below_it()
{
var config = ConfigFile.Parse("gfx.antialiasing taa\nwidth 800\nmultisampling 2\nheight 600\n");
config.MoveAfter("gfx.antialiasing", "multisampling");
Assert.Equal(new[] { "width 800", "multisampling 2", "gfx.antialiasing taa", "height 600", "" }, Lines(config));
Assert.Equal("taa", config.GetRaw("gfx.antialiasing"));
Assert.Equal("600", config.GetRaw("height"));
}
[Theory]
[InlineData("multisampling 2\nwidth 800\ngfx.antialiasing taa\n")]
[InlineData("multisampling 2\n")]
[InlineData("gfx.antialiasing taa\n")]
public void A_line_below_or_alone_stays_where_it_is(string text)
{
var config = ConfigFile.Parse(text);
config.MoveAfter("gfx.antialiasing", "multisampling");
Assert.Equal(ConfigFile.Parse(text).ToText(), config.ToText());
}
[Fact]
public void Removing_a_key_comments_every_line_that_sets_it()
{

View File

@@ -33,7 +33,7 @@ public class GraphicsPresetTests
var topKeys = store.Render(top);
var differing = lowKeys.Keys.Union(topKeys.Keys)
.Where(key => lowKeys.GetValueOrDefault(key) != topKeys.GetValueOrDefault(key))
.Where(key => !key.StartsWith("gfx.framebuffer", StringComparison.Ordinal));
.Where(key => !key.StartsWith("gfx.framebuffer", StringComparison.Ordinal) && key != "gfx.buffer.percent");
Assert.Equal(GraphicsPreset.Keys.OrderBy(k => k), differing.OrderBy(k => k));
}
@@ -103,7 +103,7 @@ public class GraphicsPresetTests
var differing = own.Keys.Union(high.Keys)
.Where(key => own.GetValueOrDefault(key) != high.GetValueOrDefault(key))
.Where(key => !GraphicsPreset.RowKeys.Contains(key))
.Where(key => !key.StartsWith("gfx.framebuffer", StringComparison.Ordinal));
.Where(key => !key.StartsWith("gfx.framebuffer", StringComparison.Ordinal) && key != "gfx.buffer.percent");
Assert.Equal(new[] { "gfx.reflections.fidelity", "gfx.reflections.framerate", "gfx.shadows.cab.range",
"gfx.smoke.fidelity", "splinefidelity" }, differing.OrderBy(k => k));
@@ -144,6 +144,113 @@ public class GraphicsPresetTests
Assert.Same(GraphicsPreset.Low, GraphicsPreset.Matching(again.Settings));
}
[Fact]
public void High_is_what_the_new_simulator_does_with_nothing_set()
{
Assert.Equal(GraphicsPreset.High.NewRenderer, GraphicsNewRenderer.Of(Defaults()));
}
[Fact]
public void The_new_renderers_scales_never_go_down()
{
void WordsClimb(string name, Func<GraphicsNewRenderer, string> word)
{
var words = SimulatorKeys.Named(name).Words!;
var places = GraphicsPreset.All.Select(p => Array.IndexOf(words, word(p.NewRenderer))).ToList();
Assert.DoesNotContain(-1, places);
Assert.Equal(places.OrderBy(place => place), places);
}
void TurnsOn(Func<GraphicsNewRenderer, bool> on)
{
var values = GraphicsPreset.All.Select(p => on(p.NewRenderer)).ToList();
Assert.Equal(values.OrderBy(value => value), values);
}
WordsClimb("gfx.lights.shadows", r => r.LampShadows);
WordsClimb("gfx.contactshadows", r => r.ContactShadows);
WordsClimb("gfx.postfx.ao", r => r.Occlusion);
WordsClimb("gfx.postfx.ao.quality", r => r.OcclusionQuality);
WordsClimb("gfx.cabprobe", r => r.CabProbe);
WordsClimb("gfx.ssr", r => r.ScreenReflections);
WordsClimb("gfx.fog", r => r.Fog);
WordsClimb("gfx.fog.quality", r => r.FogQuality);
WordsClimb("gfx.clouds", r => r.Clouds);
TurnsOn(r => r.OcclusionLighting);
TurnsOn(r => r.CabProbeReflections);
TurnsOn(r => r.RayTracing);
TurnsOn(r => r.CloudShadows);
TurnsOn(r => r.Grass);
TurnsOn(r => r.Bloom);
Climbs(p => p.NewRenderer.GrassDistance, 0);
Climbs(p => p.NewRenderer.GrassFade, 0);
Climbs(p => p.NewRenderer.GrassDensity, 0);
Climbs(p => p.NewRenderer.Precipitation, 0);
// Frames between refreshes, so the dearer the fewer.
Climbs(p => -p.NewRenderer.CabProbeInterval, 2);
}
[Fact]
public void Ultra_is_the_top_of_the_new_renderers_scales()
{
var ultra = GraphicsPreset.Ultra.NewRenderer;
double Top(string key) => SimulatorKeys.Named(key).Maximum;
string TopWord(string key) => SimulatorKeys.Named(key).Words!.Last();
Assert.Equal(TopWord("gfx.lights.shadows"), ultra.LampShadows);
Assert.Equal(TopWord("gfx.contactshadows"), ultra.ContactShadows);
Assert.Equal(TopWord("gfx.postfx.ao"), ultra.Occlusion);
Assert.Equal(TopWord("gfx.postfx.ao.quality"), ultra.OcclusionQuality);
Assert.Equal(TopWord("gfx.cabprobe"), ultra.CabProbe);
Assert.Equal(TopWord("gfx.ssr"), ultra.ScreenReflections);
Assert.Equal(TopWord("gfx.fog"), ultra.Fog);
Assert.Equal(TopWord("gfx.fog.quality"), ultra.FogQuality);
Assert.Equal(TopWord("gfx.clouds"), ultra.Clouds);
Assert.Equal(Top("gfx.grass.distance"), ultra.GrassDistance);
Assert.Equal(Top("gfx.grass.fadedistance"), ultra.GrassFade);
Assert.Equal(Top("gfx.grass.density"), ultra.GrassDensity);
Assert.Equal(Top("gfx.precipitation.particles"), ultra.Precipitation);
}
[Fact]
public void Grass_fades_out_beyond_where_it_is_drawn_in_full()
{
Assert.All(GraphicsPreset.All, p => Assert.True(p.NewRenderer.GrassFade > p.NewRenderer.GrassDistance, p.NameKey));
}
[Fact]
public void A_level_set_in_the_new_renderers_rows_is_told_from_the_others()
{
var settings = Defaults().Copy();
GraphicsPreset.Medium.ApplyTo(settings);
Assert.Same(GraphicsPreset.Medium, GraphicsPreset.Matching(settings));
settings.Extra["gfx.fog.quality"] = "ultra";
Assert.Null(GraphicsPreset.Matching(settings));
}
[Fact]
public void What_is_left_to_the_player_stays_through_a_level()
{
var settings = Defaults().Copy();
settings.Extra["gfx.antialiasing"] = "taa";
settings.Extra["gfx.framegeneration"] = "x2";
settings.Extra["gfx.vulkan.device"] = "RTX";
foreach (var preset in GraphicsPreset.All)
{
preset.ApplyTo(settings);
Assert.Same(preset, GraphicsPreset.Matching(settings));
Assert.Equal("taa", settings.Extra["gfx.antialiasing"]);
Assert.Equal("x2", settings.Extra["gfx.framegeneration"]);
Assert.Equal("RTX", settings.Extra["gfx.vulkan.device"]);
}
}
[Fact]
public void Ultra_is_the_top_of_everything_the_screen_offers()
{
@@ -503,6 +610,11 @@ public class GraphicsPresetTests
OnSteps(preset.SleeperDistance, GraphicsPreset.SleeperKey);
OnSteps(preset.ReflectedInstances, GraphicsPreset.ReflectedInstancesKey);
OnSteps(preset.ReflectedVehicles, GraphicsPreset.ReflectedVehiclesKey);
OnSteps(preset.NewRenderer.CabProbeInterval, "gfx.cabprobe.interval");
OnSteps(preset.NewRenderer.GrassDistance, "gfx.grass.distance");
OnSteps(preset.NewRenderer.GrassFade, "gfx.grass.fadedistance");
OnSteps(preset.NewRenderer.GrassDensity, "gfx.grass.density");
OnSteps(preset.NewRenderer.Precipitation, "gfx.precipitation.particles");
Assert.True(Steps.IndexOf(Steps.Anisotropy, preset.Anisotropy) >= 0, preset.NameKey);
Assert.True(Steps.IndexOf(Steps.MultisamplingLevels, preset.Multisampling) >= 0, preset.NameKey);
Assert.True(Steps.IndexOf(Steps.ReflectionUpdates, preset.ReflectionsRate) >= 0, preset.NameKey);

View File

@@ -72,6 +72,32 @@ public class SettingReachTests
Assert.Equal(expected, SettingReach.Why("sleeperdistance", Linux(renderer)));
}
[Theory]
[InlineData("vulkan", IdleReason.None)]
[InlineData("vulkan14", IdleReason.None)]
[InlineData("full", IdleReason.VulkanOnly)]
[InlineData("gl46", IdleReason.VulkanOnly)]
[InlineData("legacy", IdleReason.VulkanOnly)]
public void What_only_vulkan_reads_follows_the_renderer(string renderer, IdleReason expected)
{
Assert.Equal(expected, SettingReach.Why("gfx.dlss.preset", Linux(renderer)));
Assert.Equal(expected, SettingReach.Why("gfx.framegeneration", Linux(renderer)));
Assert.Equal(expected, SettingReach.Why("gfx.raytracing", Linux(renderer)));
// Both renderers of the new simulator read this one.
Assert.Equal(IdleReason.None, SettingReach.Why("gfx.postfx.ao", Linux(renderer)));
}
[Theory]
[InlineData("vulkan")]
[InlineData("vulkan14")]
[InlineData("opengl")]
[InlineData("gl46")]
public void The_new_simulators_renderers_read_what_the_full_one_does(string renderer)
{
Assert.Equal(IdleReason.None, SettingReach.Why("gfx.envmap.enabled", Linux(renderer)));
Assert.Equal(IdleReason.None, SettingReach.Why("multisampling", Linux(renderer)));
}
[Theory]
[InlineData("full", IdleReason.None)]
[InlineData("legacy", IdleReason.None)]

View File

@@ -858,6 +858,9 @@ public class SettingsStoreTests
[InlineData("default")]
[InlineData("simpleshader")]
[InlineData("off")]
[InlineData("opengl")]
[InlineData("gl46")]
[InlineData("vulkan14")]
public void A_renderer_word_the_simulator_reads_as_another_stays_until_changed(string word)
{
var installation = WithHome(new InMemoryFileSystem().WithFile(UserIni, $"gfxrenderer {word}\n"));
@@ -873,6 +876,7 @@ public class SettingsStoreTests
[InlineData(1, "legacy")]
[InlineData(2, "simple")]
[InlineData(3, "experimental")]
[InlineData(4, "vulkan")]
public void A_chosen_renderer_is_saved_as_the_simulator_reads_it(int engine, string saved)
{
// application.cpp knows full (or default), experimental and simple; every
@@ -888,6 +892,92 @@ public class SettingsStoreTests
Assert.Contains($"gfxrenderer {saved}\n", SavedLines(installation));
}
[Theory]
[InlineData("full", 0)]
[InlineData("gl46", 0)]
[InlineData("opengl", 0)]
[InlineData("vulkan", 4)]
[InlineData("VULKAN14", 4)]
[InlineData("simpleshader", 1)]
public void A_renderer_word_stands_where_the_simulator_reads_it(string word, int engine)
{
Assert.Equal(engine, SimulatorSettings.RenderEngineOf(word));
}
[Fact]
public void The_buffer_is_written_for_old_and_new_simulators()
{
var installation = WithHome(new InMemoryFileSystem().WithFile(UserIni, "width 1000\nheight 500\n"));
installation.SettingsStore.Load();
installation.SettingsStore.Settings.BufferScalePercent = 50;
installation.SettingsStore.Save();
string saved = SavedLines(installation);
Assert.Contains("gfx.buffer.percent 50\n", saved);
Assert.Contains("gfx.framebuffer.width 500\n", saved);
Assert.Contains("gfx.framebuffer.height 250\n", saved);
}
[Theory]
[InlineData("gfx.buffer.percent 0.5\n", 50)]
[InlineData("gfx.buffer.percent 75\n", 75)]
[InlineData("gfx.buffer.percent 10\n", 25)]
[InlineData("gfx.buffer.percent 300\n", 200)]
[InlineData("starter.bufferscale 60\n", 60)]
[InlineData("starter.bufferscale 60\ngfx.buffer.percent 80\n", 80)]
public void The_buffer_is_read_from_the_key_the_new_simulator_goes_by(string ini, int percent)
{
var installation = WithHome(new InMemoryFileSystem().WithFile(UserIni, ini));
installation.SettingsStore.Load();
Assert.Equal(percent, installation.SettingsStore.Settings.BufferScalePercent);
}
[Fact]
public void Anti_aliasing_is_saved_below_multisampling_for_the_later_line_to_count()
{
var installation = WithHome(new InMemoryFileSystem().WithFile(UserIni, "gfx.antialiasing taa\nwidth 800\nmultisampling 2\n"));
installation.SettingsStore.Load();
installation.SettingsStore.Save();
string saved = SavedLines(installation);
Assert.True(saved.IndexOf("multisampling ", StringComparison.Ordinal) < saved.IndexOf("gfx.antialiasing taa", StringComparison.Ordinal), saved);
}
[Fact]
public void Anti_aliasing_left_to_multisampling_goes_from_the_file()
{
var installation = WithHome(new InMemoryFileSystem().WithFile(UserIni, "multisampling 2\ngfx.antialiasing taa\n"));
installation.SettingsStore.Load();
installation.SettingsStore.Settings.Extra["gfx.antialiasing"] = "";
installation.SettingsStore.Save();
Assert.DoesNotContain("\ngfx.antialiasing", SavedLines(installation));
}
[Theory]
[InlineData("TAA", "taa")]
[InlineData(" dlaa ", "dlaa")]
[InlineData("", "")]
[InlineData("fxaa", "")]
public void A_choice_reads_the_words_it_offers_and_else_the_default(string raw, string word)
{
Assert.Equal(word, SimulatorKeys.Named("gfx.antialiasing").ParseChoice(raw));
}
[Theory]
[InlineData("4 6", "4 6")]
[InlineData("4 6", "4 6")]
[InlineData("5 5", "3 4")]
public void A_choice_of_two_numbers_is_one_word(string raw, string word)
{
Assert.Equal(word, SimulatorKeys.Named("gfx.postfx.ao.quality").ParseChoice(raw));
}
[Fact]
public void A_field_of_view_down_to_ten_degrees_survives_a_save()
{