diff --git a/include/rt64_layer.h b/include/rt64_layer.h index 9944669..04725e3 100644 --- a/include/rt64_layer.h +++ b/include/rt64_layer.h @@ -32,6 +32,7 @@ namespace ultramodern { void shutdown(); void set_dummy_vi(); uint32_t get_display_framerate(); + float get_resolution_scale(); void load_shader_cache(std::span cache_binary); private: RT64SetupResult setup_result; diff --git a/patches/effect_patches.c b/patches/effect_patches.c index 5fff08a..ed064b3 100644 --- a/patches/effect_patches.c +++ b/patches/effect_patches.c @@ -106,6 +106,12 @@ void Play_DrawMotionBlur(PlayState* this) { } alpha = (s32)(alpha_float * 255.0f); + // @recomp Set the dither noise strength based on the resolution scale to make it easier to see at higher resolutions. + float res_scale = recomp_get_resolution_scale(); + float dither_noise_strength = CLAMP(1.0 + (res_scale - 1.0f) / 8.0f, 1.0f, 2.0f); + // recomp_printf("res scale: %5.3f dither noise strength: %5.3f\n", res_scale, dither_noise_strength); + gEXSetDitherNoiseStrength(OVERLAY_DISP++, dither_noise_strength); + if (sMotionBlurStatus == MOTION_BLUR_PROCESS) { func_80170AE0(&this->pauseBgPreRender, &gfx, alpha); } else { diff --git a/patches/graphics.h b/patches/graphics.h index 94ff609..5d517d7 100644 --- a/patches/graphics.h +++ b/patches/graphics.h @@ -6,5 +6,6 @@ DECLARE_FUNC(float, recomp_get_aspect_ratio, float); DECLARE_FUNC(s32, recomp_get_target_framerate, s32); DECLARE_FUNC(s32, recomp_high_precision_fb_enabled); +DECLARE_FUNC(float, recomp_get_resolution_scale); #endif diff --git a/patches/syms.ld b/patches/syms.ld index aa92a7f..81418e8 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -53,3 +53,4 @@ recomp_autosave_enabled = 0x8F00008C; recomp_load_overlays = 0x8F000090; osInvalICache_recomp = 0x8F000094; recomp_high_precision_fb_enabled = 0x8F0000A8; +recomp_get_resolution_scale = 0x8F0000AC; diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index ef654b6..e18d3a0 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -107,3 +107,7 @@ extern "C" void recomp_load_overlays(uint8_t * rdram, recomp_context * ctx) { extern "C" void recomp_high_precision_fb_enabled(uint8_t * rdram, recomp_context * ctx) { _return(ctx, static_cast(ultramodern::RT64HighPrecisionFBEnabled())); } + +extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) { + _return(ctx, ultramodern::get_resolution_scale()); +} diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp index 957d704..cef0b8c 100644 --- a/ultramodern/events.cpp +++ b/ultramodern/events.cpp @@ -279,6 +279,7 @@ ultramodern::GraphicsConfig ultramodern::get_graphics_config() { } std::atomic_uint32_t display_refresh_rate = 60; +std::atomic resolution_scale = 1.0f; uint32_t ultramodern::get_target_framerate(uint32_t original) { ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config(); @@ -298,6 +299,10 @@ uint32_t ultramodern::get_display_refresh_rate() { return display_refresh_rate.load(); } +float ultramodern::get_resolution_scale() { + return resolution_scale.load(); +} + void ultramodern::load_shader_cache(std::span cache_data) { events_context.action_queue.enqueue(LoadShaderCacheAction{cache_data}); } @@ -359,6 +364,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re events_context.vi.current_buffer = events_context.vi.next_buffer; rt64.update_screen(swap_action->origin); display_refresh_rate = rt64.get_display_framerate(); + resolution_scale = rt64.get_resolution_scale(); } else if (const auto* config_action = std::get_if(&action)) { ultramodern::GraphicsConfig new_config = cur_config; diff --git a/ultramodern/rt64_layer.cpp b/ultramodern/rt64_layer.cpp index a1faa4e..1a03592 100644 --- a/ultramodern/rt64_layer.cpp +++ b/ultramodern/rt64_layer.cpp @@ -278,6 +278,24 @@ uint32_t ultramodern::RT64Context::get_display_framerate() { return app->presentQueue->ext.sharedResources->swapChainRate; } +float ultramodern::RT64Context::get_resolution_scale() { + constexpr int ReferenceHeight = 240; + switch (app->userConfig.resolution) { + case RT64::UserConfiguration::Resolution::WindowIntegerScale: + if (app->sharedQueueResources->swapChainHeight > 0) { + return std::max(float((app->sharedQueueResources->swapChainHeight + ReferenceHeight - 1) / ReferenceHeight), 1.0f); + } + else { + return 1.0f; + } + case RT64::UserConfiguration::Resolution::Manual: + return float(app->userConfig.resolutionMultiplier); + case RT64::UserConfiguration::Resolution::Original: + default: + return 1.0f; + } +} + void ultramodern::RT64Context::load_shader_cache(std::span cache_binary) { // TODO figure out how to avoid a copy here. std::istringstream cache_stream{std::string{cache_binary.data(), cache_binary.size()}}; diff --git a/ultramodern/ultramodern.hpp b/ultramodern/ultramodern.hpp index 79a550c..d0cd944 100644 --- a/ultramodern/ultramodern.hpp +++ b/ultramodern/ultramodern.hpp @@ -111,9 +111,9 @@ void sleep_milliseconds(uint32_t millis); void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point); // Graphics -void get_window_size(uint32_t& width, uint32_t& height); uint32_t get_target_framerate(uint32_t original); uint32_t get_display_refresh_rate(); +float get_resolution_scale(); void load_shader_cache(std::span cache_data); // Audio