Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 83 additions & 15 deletions code/cmdline/cmdline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "globalincs/pstypes.h"
#include "globalincs/systemvars.h"
#include "globalincs/version.h"
#include "graphics/2d.h"
#include "graphics/openxr.h"
#include "graphics/shadows.h"
#include "hud/hudconfig.h"
Expand Down Expand Up @@ -1780,38 +1781,42 @@ bool SetCmdlineParams()
}

// d3d windowed
// We need to set both the raw global (for the legacy !Using_in_game_options path) and the
// Option<T> override (for the modern path) since either backend may be the one actually consulted.
if(window_arg.found()) {
// We need to set both values since we don't know if we are going to use the new config system
//options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "0");
options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "0", "-window");
Cmdline_window = true;
}

if ( fullscreen_window_arg.found( ) )
{
//options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "1");
options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "1", "-fullscreen_window");
Cmdline_fullscreen_window = true;
Cmdline_window = false; /* Make sure no-one sets both */
}

if(render_res_arg.found() || deprecated_res_arg.found()){
const char* res_flag_name;
if (render_res_arg.found()) {
Cmdline_res = render_res_arg.str();
res_flag_name = "-render_res";
}
else {
Cmdline_res = deprecated_res_arg.str();
res_flag_name = "-res";
mprintf(("Deprecated -res argument. Use -render_res instead...\n"));
}

/*int width = 0;
int width = 0;
int height = 0;

if (sscanf(Cmdline_res, "%dx%d", &width, &height) == 2) {
SCP_string override;
sprintf(override, "{\"width\":%d,\"height\":%d}", width, height);
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override);
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override, res_flag_name);
} else {
Warning(LOCATION, "Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".\n", Cmdline_res);
}*/
}
}
if(window_res_arg.found()){
int width = 0;
Expand All @@ -1829,15 +1834,15 @@ bool SetCmdlineParams()
}
if (hdr_nits_arg.found()) {
// presence of the flag alone forces HDR output on, overriding the in-game HDR setting
options::OptionsManager::instance()->setOverride("Graphics.HDR", "true");
options::OptionsManager::instance()->setOverride("Graphics.HDR", "true", "-hdr");

if (hdr_nits_arg.has_param()) {
float paperwhite_nits = 0.0f, peak_nits = 0.0f;
if (sscanf(hdr_nits_arg.str(), "%f,%f", &paperwhite_nits, &peak_nits) == 2 && paperwhite_nits > 0.0f && peak_nits > 0.0f) {
// std::to_string always includes a decimal point, so this is a valid JSON real
// (json_unpack's "f" format rejects a bare integer like "400")
options::OptionsManager::instance()->setOverride("Graphics.HDRPaperWhite", std::to_string(paperwhite_nits));
options::OptionsManager::instance()->setOverride("Graphics.HDRPeakLuminance", std::to_string(peak_nits));
options::OptionsManager::instance()->setOverride("Graphics.HDRPaperWhite", std::to_string(paperwhite_nits), "-hdr");
options::OptionsManager::instance()->setOverride("Graphics.HDRPeakLuminance", std::to_string(peak_nits), "-hdr");
} else {
Warning(LOCATION, "Failed to parse -hdr parameter \"%s\". Must be in format \"<paperwhite_nits>,<peak_nits>\".\n", hdr_nits_arg.str());
}
Expand Down Expand Up @@ -1940,6 +1945,14 @@ bool SetCmdlineParams()
} else {
VIEWER_ZOOM_DEFAULT = DEFAULT_FOV;
}

// VIEWER_ZOOM_DEFAULT is only ever a bare float this early (before any VR/asymmetric FOV setup runs),
// but guard the variant access defensively rather than assume it.
if (std::holds_alternative<float>(VIEWER_ZOOM_DEFAULT)) {
SCP_string override;
sprintf(override, "%f", std::get<float>(VIEWER_ZOOM_DEFAULT));
options::OptionsManager::instance()->setOverride("Graphics.FOV", override, "-fov");
}
}

if ( fov_cockpit_arg.found() ) {
Expand All @@ -1950,6 +1963,16 @@ bool SetCmdlineParams()
else {
COCKPIT_ZOOM_DEFAULT = VIEWER_ZOOM_DEFAULT;
}

// Graphics.CockpitFOV's change listener only applies its value when Graphics.CockpitFOVToggle is on, so
// force that on too -- otherwise the modern options path would silently ignore -fov_cockpit.
options::OptionsManager::instance()->setOverride("Graphics.CockpitFOVToggle", "true", "-fov_cockpit");

if (std::holds_alternative<float>(COCKPIT_ZOOM_DEFAULT)) {
SCP_string override;
sprintf(override, "%f", std::get<float>(COCKPIT_ZOOM_DEFAULT));
options::OptionsManager::instance()->setOverride("Graphics.CockpitFOV", override, "-fov_cockpit");
}
}

if (vr.found()) {
Expand All @@ -1968,13 +1991,13 @@ bool SetCmdlineParams()

Cmdline_fullscreen_window = false;
Cmdline_window = true; /* Make sure no-one sets both */
removeWindowModeOption();
options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "0", "-vr");

// Turn off VSync, since we definitely don't want to be capped by the main monitor
// the OpenXR runtime should have a buffered wait which will behave, effectively,
// as a VSync to VR goggle driver
Gr_enable_vsync = false;
removeVSyncOption();
options::OptionsManager::instance()->setOverride("Graphics.VSync", "false", "-vr");

openxr_prepare();
}
Expand Down Expand Up @@ -2029,6 +2052,7 @@ bool SetCmdlineParams()
if(no_vsync_arg.found() )
{
Gr_enable_vsync = false;
options::OptionsManager::instance()->setOverride("Graphics.VSync", "false", "-no_vsync");
}

if ( normal_arg.found() ) {
Expand Down Expand Up @@ -2062,11 +2086,16 @@ bool SetCmdlineParams()
case 5:
Gr_aa_mode = AntiAliasMode::SMAA_High;
break;
case 6:
case 6:
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
}
}

SCP_string override;
sprintf(override, "%d", static_cast<int>(Gr_aa_mode));
options::OptionsManager::instance()->setOverride("Graphics.AAMode", override,
post_process_aa_preset_arg.found() ? "-aa_preset" : "-aa");
}

if (msaa_enabled_arg.found()) {
Expand All @@ -2082,6 +2111,10 @@ bool SetCmdlineParams()
Cmdline_msaa_enabled = 0;
break;
}

SCP_string override;
sprintf(override, "%d", Cmdline_msaa_enabled);
options::OptionsManager::instance()->setOverride("Graphics.MSAASamples", override, "-msaa");
}

if ( glow_arg.found() )
Expand Down Expand Up @@ -2207,21 +2240,31 @@ bool SetCmdlineParams()
if ( softparticles_arg.found() )
{
Cmdline_softparticles = 1;
options::OptionsManager::instance()->setOverride("Graphics.SoftParticles", "true", "-soft_particles");
}

if ( fb_explosions_arg.found() )
{
Gr_framebuffer_effects.set(FramebufferEffects::Shockwaves, true);
}

if (fb_thrusters_arg.found())
if (fb_thrusters_arg.found())
{
Gr_framebuffer_effects.set(FramebufferEffects::Thrusters, true);
}

if (fb_explosions_arg.found() || fb_thrusters_arg.found()) {
SCP_string override;
sprintf(override, "%llu", static_cast<unsigned long long>(Gr_framebuffer_effects.to_u64()));
options::OptionsManager::instance()->setOverride("Graphics.FramebufferEffects", override,
fb_explosions_arg.found() && fb_thrusters_arg.found() ? "-fb_explosions -fb_thrusters"
: fb_explosions_arg.found() ? "-fb_explosions" : "-fb_thrusters");
}

if ( no_postprocess_arg.found() )
{
Gr_post_processing_enabled = false;
options::OptionsManager::instance()->setOverride("Graphics.PostProcessing", "false", "-no_post_process");
}

if( reparse_mainhall_arg.found() )
Expand All @@ -2235,12 +2278,17 @@ bool SetCmdlineParams()
Shadow_quality_uses_mod_option = true;

Shadow_quality = ShadowQuality::Medium;

// Deliberately not calling setOverride() here: mod_table_init() (which runs after cmdline parsing but
// before OptionsManager::loadInitialValues()) may still adjust Shadow_quality via game_settings.tbl's
// "$Shadow Quality Default:" when Shadow_quality_uses_mod_option is set, and an override registered now
// would freeze in the Medium fallback and ignore that later adjustment.
}

if( shadow_quality_arg.found() )
{
// set that we are not using default shadow quality level --wookieejedi
Shadow_quality_uses_mod_option = false;
Shadow_quality_uses_mod_option = false;

switch (shadow_quality_arg.get_int()) {
case 0:
Expand All @@ -2263,6 +2311,12 @@ bool SetCmdlineParams()
Shadow_quality = ShadowQuality::Disabled;
break;
}

// Unlike `-enable_shadows` alone, an explicit quality level is an unambiguous hard override that
// nothing else (mod defaults included) should be able to silently override.
SCP_string override;
sprintf(override, "%d", static_cast<int>(Shadow_quality));
options::OptionsManager::instance()->setOverride("Graphics.Shadows", override, "-shadow_quality");
}

if( rt_shadows_arg.found() )
Expand All @@ -2271,21 +2325,31 @@ bool SetCmdlineParams()
// has no effect if the renderer/hardware doesn't support
// CAPABILITY_RAYTRACED_SHADOWS -- the CSM path is used either way.
Shadow_render_method = ShadowRenderMethod::Raytraced;

SCP_string override;
sprintf(override, "%d", static_cast<int>(ShadowRenderMethod::Raytraced));
options::OptionsManager::instance()->setOverride("Graphics.ShadowRenderMethod", override, "-rt_shadows");
}

if( no_deferred_lighting_arg.found() )
{
Cmdline_no_deferred_lighting = 1;
options::OptionsManager::instance()->setOverride("Graphics.DeferredLighting", "false", "-no_deferred");
}

if( deferred_lighting_cockpit_arg.found() )
{
Cmdline_deferred_lighting_cockpit = true;
options::OptionsManager::instance()->setOverride("Graphics.DeferredCockpitLighting", "true", "-deferred_cockpit");
}

if (anisotropy_level_arg.found())
if (anisotropy_level_arg.found())
{
Cmdline_aniso_level = anisotropy_level_arg.get_int();

SCP_string override;
sprintf(override, "%.1f", static_cast<float>(Cmdline_aniso_level));
options::OptionsManager::instance()->setOverride("Graphics.Anisotropy", override, "-anisotropic_filter");
}

if (frame_profile_write_file.found())
Expand Down Expand Up @@ -2372,9 +2436,13 @@ bool SetCmdlineParams()

if (vulkan.found()) {
Cmdline_graphics_api = GraphicsAPI::Vulkan;
options::OptionsManager::instance()->setOverride("Graphics.RenderAPI",
std::to_string(static_cast<int>(GraphicsAPI::Vulkan)), "-vulkan");
}
else if (opengl.found()) {
Cmdline_graphics_api = GraphicsAPI::OpenGL;
options::OptionsManager::instance()->setOverride("Graphics.RenderAPI",
std::to_string(static_cast<int>(GraphicsAPI::OpenGL)), "-opengl");
}

//Deprecated flags - CommanderDJ
Expand Down
2 changes: 0 additions & 2 deletions code/cmdline/cmdline.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ extern int Cmdline_multithreading;
enum class WeaponSpewType { NONE = 0, STANDARD, ALL };
extern WeaponSpewType Cmdline_spew_weapon_stats;

extern void removeVSyncOption();
extern void removeWindowModeOption();
extern void removeResolutionOption();
extern void removeResolutionVROption();

Expand Down
75 changes: 69 additions & 6 deletions code/graphics/2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,71 @@ const auto LightingOption __UNUSED = options::OptionBuilder<int>("Graphics.Light
.parser(parse_lighting_func)
.finish();

// The Graphics.RenderAPI option only exists in builds that actually support choosing a backend at runtime.
// In an OpenGL-only build there's nothing to choose between, so there's no point cluttering the options menu
// with a one-item dropdown -- gr_get_configured_render_api() just returns the fixed default in that case.
#ifdef WITH_VULKAN

static GraphicsAPI Gr_configured_render_api = GraphicsAPI::OpenGL;

static void parse_render_api_func()
{
SCP_string value;
stuff_string(value, F_NAME);
if (lcase_equal(value, "opengl")) {
Gr_configured_render_api = GraphicsAPI::OpenGL;
} else if (lcase_equal(value, "vulkan")) {
Gr_configured_render_api = GraphicsAPI::Vulkan;
} else {
error_display(0, "%s is an invalid render API", value.c_str());
}
}

static SCP_vector<GraphicsAPI> render_api_enumerator() { return {GraphicsAPI::OpenGL, GraphicsAPI::Vulkan}; }

static SCP_string render_api_display(const GraphicsAPI& api)
{
switch (api) {
case GraphicsAPI::Vulkan:
return "Vulkan";
case GraphicsAPI::OpenGL:
default:
return "OpenGL";
}
}

// Read directly via getValue() at the one call site that needs it (freespace.cpp, right before gr_init()),
// rather than bound to a global via a change listener -- like Resolution/Anisotropy, this can't take effect
// without a restart, so there is no "live" value to keep in sync.
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
static auto RenderAPIOption __UNUSED = options::OptionBuilder<GraphicsAPI>("Graphics.RenderAPI",
SCP_string("Render API"),
SCP_string("Selects the rendering backend used by the engine. Requires a restart to take effect."))
.category(std::make_pair("Graphics", 1825))
.level(options::ExpertLevel::Advanced)
.enumerator(render_api_enumerator)
.display(render_api_display)
.flags({options::OptionFlags::ForceMultiValueSelection})
.default_func([]() { return Gr_configured_render_api; })
.importance(99)
.parser(parse_render_api_func)
.finish();

GraphicsAPI gr_get_configured_render_api()
{
return RenderAPIOption->getValue();
}

#else

GraphicsAPI gr_get_configured_render_api()
{
// No backend choice in an OpenGL-only build; Default resolves to OpenGL in gr_init().
return GraphicsAPI::Default;
}

#endif

os::ViewportState Gr_configured_window_state = os::ViewportState::Fullscreen;

static bool mode_change_func(os::ViewportState state, bool initial)
Expand Down Expand Up @@ -273,11 +338,6 @@ static auto WindowModeOption __UNUSED = options::OptionBuilder<os::ViewportState
.parser(parse_window_mode_func)
.finish();

void removeWindowModeOption()
{
options::OptionsManager::instance()->removeOption(WindowModeOption);
}

// coverity[GLOBAL_INIT_ORDER] -- safe; Hook::Factory() uses Meyers singleton
const std::shared_ptr<scripting::OverridableHook<>> OnFrameHook = scripting::OverridableHook<>::Factory(
"On Frame", "Called every frame as the last action before showing the frame result to the user.", {}, std::nullopt, CHA_ONFRAME);
Expand Down Expand Up @@ -1838,7 +1898,10 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, GraphicsAPI
height = res.height;
removeResolutionVROption();
}
//TODO set d_mode from Ingame Options if available here

// Pick the rendering backend from the in-game Graphics.RenderAPI option (or the mod's default
// settings table). A -vulkan/-opengl command line flag still wins below via Cmdline_graphics_api.
d_mode = gr_get_configured_render_api();
} else if ( !Is_standalone ) {
// We cannot continue without this, quit, but try to help the user out first
ptr = os_config_read_string(nullptr, NOX("VideocardFs2open"), nullptr);
Expand Down
4 changes: 4 additions & 0 deletions code/graphics/2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,10 @@ extern const char *Resolution_prefixes[GR_NUM_RESOLUTIONS];
extern bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, GraphicsAPI d_mode = GraphicsAPI::Default,
int d_width = GR_DEFAULT, int d_height = GR_DEFAULT, int d_depth = GR_DEFAULT);

// The render API (GraphicsAPI::OpenGL/GraphicsAPI::Vulkan) selected via the "Graphics.RenderAPI" in-game option (or a mod's
// default settings table). Does not account for the -vulkan command line override; see Cmdline_vulkan for that.
extern GraphicsAPI gr_get_configured_render_api();

extern void gr_screen_resize(int width, int height);
extern int gr_get_resolution_class(int width, int height);

Expand Down
Loading
Loading