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 {
// 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 {
public:
explicit AudioDriver(memory::Memory* memory);

View file

@ -11,8 +11,22 @@
#include <rex/audio/audio_driver.h>
#include <atomic>
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() = default;

View file

@ -137,6 +137,19 @@ bool SDLAudioDriver::Initialize() {
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;
}

View file

@ -26,6 +26,7 @@
#include <imgui.h>
#include <rex/cvar.h>
#include <rex/logging.h>
#include <rex/audio/audio_driver.h>
#include <rex/ui/presenter.h>
#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};
// Shadow quality: dynamic-shadow toggle + cascade tile resolution. The
// game's own atlas is three 512 tiles ("Console").
constexpr std::array<const char*, 4> kShadowQualityLabels = {"Off", "Console (512)",
"High (1024)", "Ultra (2048)"};
constexpr std::array<int32_t, 4> kShadowQualityTiles = {512, 512, 1024, 2048};
// game's own atlas is three 512 tiles ("Console"); tile 0 = auto (512 x
// the output resolution scale, the emulated renderer's effective raster).
constexpr std::array<const char*, 6> kShadowQualityLabels = {
"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).
constexpr std::array<const char*, 4> kAudioBufferLabels = {
@ -332,15 +334,21 @@ int ShadowQualityIndexFrom(bool enabled, int32_t tile) {
if (!enabled) {
return 0;
}
if (tile <= 512) {
return 1;
if (tile <= 0) {
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() {
if (!HasShadowQualityCvars()) {
return 2;
return 1;
}
return ShadowQualityIndexFrom(
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.desc =
"Dynamic character/prop shadow resolution in the native renderer. "
"Console matches the original game's shadow maps.";
for (const char* label : kShadowQualityLabels) {
row.options.push_back(label);
"Auto follows the Resolution Scale setting; 512 matches the "
"original game's shadow maps.";
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.reset = [this] {
shadow_quality_index_ = ShadowQualityIndexFrom(
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));
}
@ -1419,8 +1442,17 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
row.desc =
"Audio device buffer length. Larger buffers add a little latency but "
"fix crackling on machines with scheduling hiccups.";
for (const char* label : kAudioBufferLabels) {
row.options.push_back(label);
for (size_t i = 0; i < kAudioBufferLabels.size(); ++i) {
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.reset = [this] {