diff --git a/librecomp/include/disable_warnings.h b/librecomp/include/disable_warnings.h deleted file mode 100644 index 6526983..0000000 --- a/librecomp/include/disable_warnings.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wimplicit-function-declaration" -#endif diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index 2234f82..715e458 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -101,15 +101,37 @@ std::filesystem::path get_save_file_path() { return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin"); } -void update_save_file() { - std::ofstream save_file{ get_save_file_path(), std::ios_base::binary }; +std::filesystem::path get_save_file_path_temp() { + return config_path / save_folder / (recomp::current_game_id() + u8".bin.temp"); +} - if (save_file.good()) { - std::lock_guard lock{ save_context.save_buffer_mutex }; - save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size()); - } else { - fprintf(stderr, "Failed to save!\n"); - std::exit(EXIT_FAILURE); +std::filesystem::path get_save_file_path_backup() { + return config_path / save_folder / (recomp::current_game_id() + u8".bin.bak"); +} + +void update_save_file() { + { + std::ofstream save_file{ get_save_file_path_temp(), std::ios_base::binary }; + + if (save_file.good()) { + std::lock_guard lock{ save_context.save_buffer_mutex }; + save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size()); + } + else { + ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions. If you have moved your appdata folder to Dropbox or similar, this can cause issues."); + } + } + + std::error_code ec; + if (std::filesystem::exists(get_save_file_path(), ec)) { + std::filesystem::copy_file(get_save_file_path(), get_save_file_path_backup(), std::filesystem::copy_options::overwrite_existing, ec); + if (ec) { + printf("[ERROR] Failed to copy save file backup\n"); + } + } + std::filesystem::copy_file(get_save_file_path_temp(), get_save_file_path(), std::filesystem::copy_options::overwrite_existing, ec); + if (ec) { + ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions. If you have moved your appdata folder to Dropbox or similar, this can cause issues."); } } @@ -176,6 +198,7 @@ void save_clear(uint32_t start, uint32_t size, char value) { void ultramodern::init_saving(RDRAM_ARG1) { std::filesystem::path save_file_path = get_save_file_path(); + std::filesystem::path save_file_path_backup = get_save_file_path_backup(); // Ensure the save file directory exists. std::filesystem::create_directories(save_file_path.parent_path()); @@ -185,8 +208,14 @@ void ultramodern::init_saving(RDRAM_ARG1) { if (save_file.good()) { save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size()); } else { - // Otherwise clear the save file to all zeroes. - save_context.save_buffer.fill(0); + // Reading the save file faield, so try to read the backup save file. + std::ifstream save_file_backup{ save_file_path_backup, std::ios_base::binary }; + if (save_file_backup.good()) { + save_file_backup.read(save_context.save_buffer.data(), save_context.save_buffer.size()); + } else { + // Otherwise clear the save file to all zeroes. + save_context.save_buffer.fill(0); + } } save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; diff --git a/rt64 b/rt64 index ca26fb8..1adcbea 160000 --- a/rt64 +++ b/rt64 @@ -1 +1 @@ -Subproject commit ca26fb8096b9af117c668e8dfaa49fdde815c3b7 +Subproject commit 1adcbea31a04f2403da729eb5dfed3950dd7ec52 diff --git a/ultramodern/CMakeLists.txt b/ultramodern/CMakeLists.txt index 94356b1..336fd16 100644 --- a/ultramodern/CMakeLists.txt +++ b/ultramodern/CMakeLists.txt @@ -61,6 +61,7 @@ if (WIN32) target_link_directories(ultramodern PRIVATE ${sdl2_SOURCE_DIR}/lib/x64 ) + add_compile_definitions(NOMINMAX) elseif (APPLE) find_package(SDL2 REQUIRED) target_include_directories(ultramodern PRIVATE ${SDL2_INCLUDE_DIRS}) diff --git a/ultramodern/include/ultramodern/config.hpp b/ultramodern/include/ultramodern/config.hpp index a8b99e2..7212034 100644 --- a/ultramodern/include/ultramodern/config.hpp +++ b/ultramodern/include/ultramodern/config.hpp @@ -28,6 +28,12 @@ namespace ultramodern { Vulkan, OptionCount }; + enum class HighPrecisionFramebuffer { + Auto, + On, + Off, + OptionCount + }; struct GraphicsConfig { Resolution res_option; @@ -38,6 +44,7 @@ namespace ultramodern { RT64::UserConfiguration::AspectRatio ar_option; RT64::UserConfiguration::Antialiasing msaa_option; RT64::UserConfiguration::RefreshRate rr_option; + HighPrecisionFramebuffer hpfb_option; int rr_manual_value; int ds_option; bool developer_mode; @@ -70,6 +77,12 @@ namespace ultramodern { {ultramodern::GraphicsApi::D3D12, "D3D12"}, {ultramodern::GraphicsApi::Vulkan, "Vulkan"}, }); + + NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::HighPrecisionFramebuffer, { + {ultramodern::HighPrecisionFramebuffer::Auto, "Auto"}, + {ultramodern::HighPrecisionFramebuffer::On, "On"}, + {ultramodern::HighPrecisionFramebuffer::Off, "Off"}, + }); }; #endif diff --git a/ultramodern/include/ultramodern/rt64_layer.h b/ultramodern/include/ultramodern/rt64_layer.h index 0928d06..4b9f31a 100644 --- a/ultramodern/include/ultramodern/rt64_layer.h +++ b/ultramodern/include/ultramodern/rt64_layer.h @@ -32,14 +32,16 @@ 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; std::unique_ptr app; }; - + RT64::UserConfiguration::Antialiasing RT64MaxMSAA(); bool RT64SamplePositionsSupported(); + bool RT64HighPrecisionFBEnabled(); } void set_rt64_hooks(); diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index 547fba1..871abee 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -122,9 +122,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 diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 7fba7b6..9ca13c2 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -241,6 +241,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(); @@ -260,6 +261,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}); } @@ -322,6 +327,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/src/rt64_layer.cpp b/ultramodern/src/rt64_layer.cpp index 45290b1..adc09f9 100644 --- a/ultramodern/src/rt64_layer.cpp +++ b/ultramodern/src/rt64_layer.cpp @@ -1,6 +1,5 @@ #include #include -// #include #define HLSL_CPU #include "hle/rt64_application.h" @@ -11,6 +10,7 @@ ultramodern::RT64Context::~RT64Context() = default; static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None; static bool sample_positions_supported = false; +static bool high_precision_fb_enabled = false; static uint8_t DMEM[0x1000]; static uint8_t IMEM[0x1000]; @@ -58,6 +58,19 @@ RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampl return RT64::UserConfiguration::Antialiasing::None; } +RT64::UserConfiguration::InternalColorFormat to_rt64(ultramodern::HighPrecisionFramebuffer option) { + switch (option) { + case ultramodern::HighPrecisionFramebuffer::Off: + return RT64::UserConfiguration::InternalColorFormat::Standard; + case ultramodern::HighPrecisionFramebuffer::On: + return RT64::UserConfiguration::InternalColorFormat::High; + case ultramodern::HighPrecisionFramebuffer::Auto: + return RT64::UserConfiguration::InternalColorFormat::Automatic; + default: + return RT64::UserConfiguration::InternalColorFormat::OptionCount; + } +} + void set_application_user_config(RT64::Application* application, const ultramodern::GraphicsConfig& config) { switch (config.res_option) { default: @@ -95,6 +108,7 @@ void set_application_user_config(RT64::Application* application, const ultramode application->userConfig.antialiasing = config.msaa_option; application->userConfig.refreshRate = config.rr_option; application->userConfig.refreshRateTarget = config.rr_manual_value; + application->userConfig.internalColorFormat = to_rt64(config.hpfb_option); } ultramodern::RT64SetupResult map_setup_result(RT64::Application::SetupResult rt64_result) { @@ -223,6 +237,8 @@ ultramodern::RT64Context::RT64Context(uint8_t* rdram, ultramodern::WindowHandle device_max_msaa = RT64::UserConfiguration::Antialiasing::None; sample_positions_supported = false; } + + high_precision_fb_enabled = app->shaderLibrary->usesHDR; } void ultramodern::RT64Context::send_dl(const OSTask* task) { @@ -268,6 +284,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()}}; @@ -285,3 +319,7 @@ RT64::UserConfiguration::Antialiasing ultramodern::RT64MaxMSAA() { bool ultramodern::RT64SamplePositionsSupported() { return sample_positions_supported; } + +bool ultramodern::RT64HighPrecisionFBEnabled() { + return high_precision_fb_enabled; +}