diff --git a/include/rex/audio/audio_driver.h b/include/rex/audio/audio_driver.h index 37ccbf2..4abbb6f 100644 --- a/include/rex/audio/audio_driver.h +++ b/include/rex/audio/audio_driver.h @@ -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); diff --git a/src/audio/audio_driver.cpp b/src/audio/audio_driver.cpp index f01eb6a..b4ca04f 100644 --- a/src/audio/audio_driver.cpp +++ b/src/audio/audio_driver.cpp @@ -11,8 +11,22 @@ #include +#include + namespace rex::audio { +namespace { +std::atomic 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; diff --git a/src/audio/sdl/sdl_audio_driver.cpp b/src/audio/sdl/sdl_audio_driver.cpp index bf462b8..0e7cfce 100644 --- a/src/audio/sdl/sdl_audio_driver.cpp +++ b/src/audio/sdl/sdl_audio_driver.cpp @@ -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; } diff --git a/src/ui/overlay/simple_settings_overlay.cpp b/src/ui/overlay/simple_settings_overlay.cpp index fa64b48..0a1a834 100644 --- a/src/ui/overlay/simple_settings_overlay.cpp +++ b/src/ui/overlay/simple_settings_overlay.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -72,10 +73,11 @@ constexpr std::array kMsaaLabels = {"Off", "2x", "4x", "8x"}; constexpr std::array 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 kShadowQualityLabels = {"Off", "Console (512)", - "High (1024)", "Ultra (2048)"}; -constexpr std::array 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 kShadowQualityLabels = { + "Off", "Auto", "512 (console)", "1024", "1536", "2048"}; +constexpr std::array kShadowQualityTiles = {0, 0, 512, 1024, 1536, 2048}; // Audio device buffer sizes in sample frames (0 = backend default). constexpr std::array 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("skate3_native_render_scene_shadows"), @@ -1214,15 +1222,30 @@ void SimpleSettingsDialog::BuildRows(std::vector& 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(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& 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] {