ui: Auto shadow-quality tier + resolved-value labels in the settings menu

This commit is contained in:
mchughalex 2026-07-17 06:35:10 +01:00
parent 02e6166402
commit 0084c84542
4 changed files with 82 additions and 14 deletions

View file

@ -16,6 +16,15 @@
namespace rex::audio { namespace rex::audio {
// Device buffer length (sample frames) the backend chose when
// audio_device_sample_frames ran at 0 (= backend default). 0 while unknown:
// before device init, on init failure, or when an explicit size was
// requested (the device then just reflects the request). UI consumers use
// this to display what "auto" resolves to on this machine; drivers publish
// it at device init.
int32_t AutoDeviceSampleFrames();
void SetAutoDeviceSampleFrames(int32_t sample_frames);
class AudioDriver { class AudioDriver {
public: public:
explicit AudioDriver(memory::Memory* memory); explicit AudioDriver(memory::Memory* memory);

View file

@ -11,8 +11,22 @@
#include <rex/audio/audio_driver.h> #include <rex/audio/audio_driver.h>
#include <atomic>
namespace rex::audio { namespace rex::audio {
namespace {
std::atomic<int32_t> g_auto_device_sample_frames{0};
} // namespace
int32_t AutoDeviceSampleFrames() {
return g_auto_device_sample_frames.load(std::memory_order_relaxed);
}
void SetAutoDeviceSampleFrames(int32_t sample_frames) {
g_auto_device_sample_frames.store(sample_frames, std::memory_order_relaxed);
}
AudioDriver::AudioDriver(memory::Memory* memory) : memory_(memory) {} AudioDriver::AudioDriver(memory::Memory* memory) : memory_(memory) {}
AudioDriver::~AudioDriver() = default; AudioDriver::~AudioDriver() = default;

View file

@ -137,6 +137,19 @@ bool SDLAudioDriver::Initialize() {
return false; return false;
} }
// Publish the buffer length the backend chose so the settings UI can show
// what "auto" resolves to, only meaningful when no explicit size was
// requested (the device otherwise just reflects the request). Queried
// after the stereo fallback so it reflects the device actually in use.
if (requested_sample_frames <= 0) {
SDL_AudioSpec active_spec = {};
int device_sample_frames = 0;
if (SDL_GetAudioDeviceFormat(sdl_device, &active_spec, &device_sample_frames) &&
device_sample_frames > 0) {
SetAutoDeviceSampleFrames(device_sample_frames);
}
}
return true; return true;
} }

View file

@ -26,6 +26,7 @@
#include <imgui.h> #include <imgui.h>
#include <rex/cvar.h> #include <rex/cvar.h>
#include <rex/logging.h> #include <rex/logging.h>
#include <rex/audio/audio_driver.h>
#include <rex/ui/presenter.h> #include <rex/ui/presenter.h>
#include <toml++/toml.hpp> #include <toml++/toml.hpp>
@ -72,10 +73,11 @@ constexpr std::array<const char*, 4> kMsaaLabels = {"Off", "2x", "4x", "8x"};
constexpr std::array<int32_t, 4> kMsaaSamples = {1, 2, 4, 8}; constexpr std::array<int32_t, 4> kMsaaSamples = {1, 2, 4, 8};
// Shadow quality: dynamic-shadow toggle + cascade tile resolution. The // Shadow quality: dynamic-shadow toggle + cascade tile resolution. The
// game's own atlas is three 512 tiles ("Console"). // game's own atlas is three 512 tiles ("Console"); tile 0 = auto (512 x
constexpr std::array<const char*, 4> kShadowQualityLabels = {"Off", "Console (512)", // the output resolution scale, the emulated renderer's effective raster).
"High (1024)", "Ultra (2048)"}; constexpr std::array<const char*, 6> kShadowQualityLabels = {
constexpr std::array<int32_t, 4> kShadowQualityTiles = {512, 512, 1024, 2048}; "Off", "Auto", "512 (console)", "1024", "1536", "2048"};
constexpr std::array<int32_t, 6> kShadowQualityTiles = {0, 0, 512, 1024, 1536, 2048};
// Audio device buffer sizes in sample frames (0 = backend default). // Audio device buffer sizes in sample frames (0 = backend default).
constexpr std::array<const char*, 4> kAudioBufferLabels = { constexpr std::array<const char*, 4> kAudioBufferLabels = {
@ -332,15 +334,21 @@ int ShadowQualityIndexFrom(bool enabled, int32_t tile) {
if (!enabled) { if (!enabled) {
return 0; return 0;
} }
if (tile <= 512) { if (tile <= 0) {
return 1; return 1; // auto
} }
return tile <= 1024 ? 2 : 3; if (tile <= 512) {
return 2;
}
if (tile <= 1024) {
return 3;
}
return tile <= 1536 ? 4 : 5;
} }
int ShadowQualityIndexFromCvar() { int ShadowQualityIndexFromCvar() {
if (!HasShadowQualityCvars()) { if (!HasShadowQualityCvars()) {
return 2; return 1;
} }
return ShadowQualityIndexFrom( return ShadowQualityIndexFrom(
rex::cvar::Query<bool>("skate3_native_render_scene_shadows"), rex::cvar::Query<bool>("skate3_native_render_scene_shadows"),
@ -1214,15 +1222,30 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
row.label = "Shadow Quality"; row.label = "Shadow Quality";
row.desc = row.desc =
"Dynamic character/prop shadow resolution in the native renderer. " "Dynamic character/prop shadow resolution in the native renderer. "
"Console matches the original game's shadow maps."; "Auto follows the Resolution Scale setting; 512 matches the "
for (const char* label : kShadowQualityLabels) { "original game's shadow maps.";
row.options.push_back(label); for (size_t i = 0; i < kShadowQualityLabels.size(); ++i) {
if (i == 1) {
// Auto = the game's 512 tiles at the render resolution scale;
// the renderer sizes the atlas from its scaled output (see the
// shadow_tile cvar), so the label follows the Resolution Scale
// row's STAGED selection: both apply on restart, and changing
// the scale updates what Auto reads as immediately.
const int scale_index = std::clamp(
resolution_scale_index_, 0,
static_cast<int>(kResolutionScales.size()) - 1);
const uint32_t auto_tile = std::min(
512u * uint32_t(kResolutionScales[scale_index]), 4096u);
row.options.push_back("Auto (" + std::to_string(auto_tile) + ")");
} else {
row.options.push_back(kShadowQualityLabels[i]);
}
} }
row.index = &shadow_quality_index_; row.index = &shadow_quality_index_;
row.reset = [this] { row.reset = [this] {
shadow_quality_index_ = ShadowQualityIndexFrom( shadow_quality_index_ = ShadowQualityIndexFrom(
CvarDefaultBool("skate3_native_render_scene_shadows", true), CvarDefaultBool("skate3_native_render_scene_shadows", true),
int32_t(CvarDefaultDouble("skate3_native_render_scene_shadow_tile", 1024.0))); int32_t(CvarDefaultDouble("skate3_native_render_scene_shadow_tile", 0.0)));
}; };
rows.push_back(std::move(row)); rows.push_back(std::move(row));
} }
@ -1419,8 +1442,17 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
row.desc = row.desc =
"Audio device buffer length. Larger buffers add a little latency but " "Audio device buffer length. Larger buffers add a little latency but "
"fix crackling on machines with scheduling hiccups."; "fix crackling on machines with scheduling hiccups.";
for (const char* label : kAudioBufferLabels) { for (size_t i = 0; i < kAudioBufferLabels.size(); ++i) {
row.options.push_back(label); if (i == 0) {
// The backend publishes the buffer it chose when running in auto
// mode; 0 while unknown (explicit size active, or no device yet).
const int32_t auto_frames = rex::audio::AutoDeviceSampleFrames();
row.options.push_back(auto_frames > 0
? "Auto (" + std::to_string(auto_frames) + ")"
: std::string("Auto"));
} else {
row.options.push_back(kAudioBufferLabels[i]);
}
} }
row.index = &audio_buffer_index_; row.index = &audio_buffer_index_;
row.reset = [this] { row.reset = [this] {