Update to latest N64ModernRuntime (#392)

- updates to new controller changes
- updates to new render context changes
- updates to new controller number changes
- fix for crash on save thread
This commit is contained in:
David Chavez 2024-06-12 09:40:14 +02:00 committed by GitHub
parent b31cf0a76b
commit 19fcd9bf31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 515 additions and 64 deletions

View File

@ -120,6 +120,7 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/main/main.cpp ${CMAKE_SOURCE_DIR}/src/main/main.cpp
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp ${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp ${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp
${CMAKE_SOURCE_DIR}/src/game/input.cpp ${CMAKE_SOURCE_DIR}/src/game/input.cpp
${CMAKE_SOURCE_DIR}/src/game/controls.cpp ${CMAKE_SOURCE_DIR}/src/game/controls.cpp

View File

@ -9,6 +9,8 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include "ultramodern/input.hpp"
#include "json/json.hpp" #include "json/json.hpp"
namespace recomp { namespace recomp {
@ -150,11 +152,13 @@ namespace recomp {
InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device); InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device);
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value); void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out); bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out);
void set_rumble(bool); void set_rumble(int controller_num, bool);
void update_rumble(); void update_rumble();
void handle_events(); void handle_events();
ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num);
// Rumble strength ranges from 0 to 100. // Rumble strength ranges from 0 to 100.
int get_rumble_strength(); int get_rumble_strength();
void set_rumble_strength(int strength); void set_rumble_strength(int strength);

View File

@ -119,7 +119,9 @@ namespace recompui {
void set_cont_active(bool active); void set_cont_active(bool active);
void activate_mouse(); void activate_mouse();
void message_box(const char* msg); void message_box(const char* msg);
void set_render_hooks();
} }
#endif #endif

42
include/zelda_render.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef __ZELDA_RENDER_H__
#define __ZELDA_RENDER_H__
#include "common/rt64_user_configuration.h"
#include "ultramodern/renderer_context.hpp"
namespace RT64 {
struct Application;
}
namespace zelda64 {
namespace renderer {
class RT64Context : public ultramodern::renderer::RendererContext {
public:
~RT64Context() override;
RT64Context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
bool valid() override { return static_cast<bool>(app); }
bool update_config(const ultramodern::renderer::GraphicsConfig &old_config, const ultramodern::renderer::GraphicsConfig &new_config) override;
void enable_instant_present() override;
void send_dl(const OSTask *task) override;
void update_screen(uint32_t vi_origin) override;
void shutdown() override;
uint32_t get_display_framerate() const override;
float get_resolution_scale() const override;
void load_shader_cache(std::span<const char> cache_binary) override;
protected:
std::unique_ptr<RT64::Application> app;
};
std::unique_ptr<ultramodern::renderer::RendererContext> create_render_context(uint8_t *rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode);
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
bool RT64SamplePositionsSupported();
bool RT64HighPrecisionFBEnabled();
}
}
#endif

@ -1 +1 @@
Subproject commit ec7e81b45d9a622cb3e45865ce5d7d3536b26534 Subproject commit 0c1811ca6f8291c6608f1d6626a73e863902ece9

View File

@ -1,6 +1,7 @@
#include "zelda_config.h" #include "zelda_config.h"
#include "recomp_input.h" #include "recomp_input.h"
#include "zelda_sound.h" #include "zelda_sound.h"
#include "zelda_render.h"
#include "ultramodern/config.hpp" #include "ultramodern/config.hpp"
#include "librecomp/files.hpp" #include "librecomp/files.hpp"
#include <filesystem> #include <filesystem>
@ -20,21 +21,21 @@ constexpr std::u8string_view controls_filename = u8"controls.json";
constexpr std::u8string_view sound_filename = u8"sound.json"; constexpr std::u8string_view sound_filename = u8"sound.json";
constexpr std::u8string_view program_id = u8"Zelda64Recompiled"; constexpr std::u8string_view program_id = u8"Zelda64Recompiled";
constexpr auto res_default = ultramodern::Resolution::Auto; constexpr auto res_default = ultramodern::renderer::Resolution::Auto;
constexpr auto hr_default = ultramodern::HUDRatioMode::Clamp16x9; constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9;
constexpr auto api_default = ultramodern::GraphicsApi::Auto; constexpr auto api_default = ultramodern::renderer::GraphicsApi::Auto;
constexpr auto ar_default = RT64::UserConfiguration::AspectRatio::Expand; constexpr auto ar_default = ultramodern::renderer::AspectRatio::Expand;
constexpr auto msaa_default = RT64::UserConfiguration::Antialiasing::MSAA2X; constexpr auto msaa_default = ultramodern::renderer::Antialiasing::MSAA2X;
constexpr auto rr_default = RT64::UserConfiguration::RefreshRate::Display; constexpr auto rr_default = ultramodern::renderer::RefreshRate::Display;
constexpr auto hpfb_default = ultramodern::HighPrecisionFramebuffer::Auto; constexpr auto hpfb_default = ultramodern::renderer::HighPrecisionFramebuffer::Auto;
constexpr int ds_default = 1; constexpr int ds_default = 1;
constexpr int rr_manual_default = 60; constexpr int rr_manual_default = 60;
constexpr bool developer_mode_default = false; constexpr bool developer_mode_default = false;
static bool is_steam_deck = false; static bool is_steam_deck = false;
ultramodern::WindowMode wm_default() { ultramodern::renderer::WindowMode wm_default() {
return is_steam_deck ? ultramodern::WindowMode::Fullscreen : ultramodern::WindowMode::Windowed; return is_steam_deck ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed;
} }
#ifdef __gnu_linux__ #ifdef __gnu_linux__
@ -86,7 +87,7 @@ void call_if_key_exists(void (*func)(T), const json& j, const std::string& key)
} }
namespace ultramodern { namespace ultramodern {
void to_json(json& j, const GraphicsConfig& config) { void to_json(json& j, const renderer::GraphicsConfig& config) {
j = json{ j = json{
{"res_option", config.res_option}, {"res_option", config.res_option},
{"wm_option", config.wm_option}, {"wm_option", config.wm_option},
@ -102,7 +103,7 @@ namespace ultramodern {
}; };
} }
void from_json(const json& j, GraphicsConfig& config) { void from_json(const json& j, renderer::GraphicsConfig& config) {
config.res_option = from_or_default(j, "res_option", res_default); config.res_option = from_or_default(j, "res_option", res_default);
config.wm_option = from_or_default(j, "wm_option", wm_default()); config.wm_option = from_or_default(j, "wm_option", wm_default());
config.hr_option = from_or_default(j, "hr_option", hr_default); config.hr_option = from_or_default(j, "hr_option", hr_default);
@ -302,7 +303,7 @@ void zelda64::reset_kb_input_bindings() {
} }
void reset_graphics_options() { void reset_graphics_options() {
ultramodern::GraphicsConfig new_config{}; ultramodern::renderer::GraphicsConfig new_config{};
new_config.res_option = res_default; new_config.res_option = res_default;
new_config.wm_option = wm_default(); new_config.wm_option = wm_default();
new_config.hr_option = hr_default; new_config.hr_option = hr_default;
@ -313,12 +314,12 @@ void reset_graphics_options() {
new_config.hpfb_option = hpfb_default; new_config.hpfb_option = hpfb_default;
new_config.rr_manual_value = rr_manual_default; new_config.rr_manual_value = rr_manual_default;
new_config.developer_mode = developer_mode_default; new_config.developer_mode = developer_mode_default;
ultramodern::set_graphics_config(new_config); ultramodern::renderer::set_graphics_config(new_config);
} }
bool save_graphics_config(const std::filesystem::path& path) { bool save_graphics_config(const std::filesystem::path& path) {
nlohmann::json config_json{}; nlohmann::json config_json{};
ultramodern::to_json(config_json, ultramodern::get_graphics_config()); ultramodern::to_json(config_json, ultramodern::renderer::get_graphics_config());
return save_json_with_backups(path, config_json); return save_json_with_backups(path, config_json);
} }
@ -328,9 +329,9 @@ bool load_graphics_config(const std::filesystem::path& path) {
return false; return false;
} }
ultramodern::GraphicsConfig new_config{}; ultramodern::renderer::GraphicsConfig new_config{};
ultramodern::from_json(config_json, new_config); ultramodern::from_json(config_json, new_config);
ultramodern::set_graphics_config(new_config); ultramodern::renderer::set_graphics_config(new_config);
return true; return true;
} }

View File

@ -75,11 +75,15 @@ void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, re
} }
} }
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) { bool recomp::get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out) {
uint16_t cur_buttons = 0; uint16_t cur_buttons = 0;
float cur_x = 0.0f; float cur_x = 0.0f;
float cur_y = 0.0f; float cur_y = 0.0f;
if (controller_num != 0) {
return false;
}
if (!recomp::game_input_disabled()) { if (!recomp::game_input_disabled()) {
for (size_t i = 0; i < n64_button_values.size(); i++) { for (size_t i = 0; i < n64_button_values.size(); i++) {
size_t input_index = (size_t)GameInput::N64_BUTTON_START + i; size_t input_index = (size_t)GameInput::N64_BUTTON_START + i;
@ -107,4 +111,6 @@ void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
*buttons_out = cur_buttons; *buttons_out = cur_buttons;
*x_out = std::clamp(cur_x, -1.0f, 1.0f); *x_out = std::clamp(cur_x, -1.0f, 1.0f);
*y_out = std::clamp(cur_y, -1.0f, 1.0f); *y_out = std::clamp(cur_y, -1.0f, 1.0f);
return true;
} }

View File

@ -446,8 +446,25 @@ void recomp::poll_inputs() {
#endif #endif
} }
void recomp::set_rumble(bool on) { void recomp::set_rumble(int controller_num, bool on) {
InputState.rumble_active = on; if (controller_num == 0) {
InputState.rumble_active = on;
}
}
ultramodern::input::connected_device_info_t recomp::get_connected_device_info(int controller_num) {
switch (controller_num) {
case 0:
return ultramodern::input::connected_device_info_t {
.connected_device = ultramodern::input::Device::Controller,
.connected_pak = ultramodern::input::Pak::RumblePak,
};
}
return ultramodern::input::connected_device_info_t {
.connected_device = ultramodern::input::Device::None,
.connected_pak = ultramodern::input::Pak::None,
};
} }
static float smoothstep(float from, float to, float amount) { static float smoothstep(float from, float to, float amount) {

View File

@ -5,6 +5,7 @@
#include "zelda_config.h" #include "zelda_config.h"
#include "recomp_input.h" #include "recomp_input.h"
#include "recomp_ui.h" #include "recomp_ui.h"
#include "zelda_render.h"
#include "zelda_sound.h" #include "zelda_sound.h"
#include "librecomp/helpers.hpp" #include "librecomp/helpers.hpp"
#include "../patches/input.h" #include "../patches/input.h"
@ -12,7 +13,6 @@
#include "../patches/sound.h" #include "../patches/sound.h"
#include "ultramodern/ultramodern.hpp" #include "ultramodern/ultramodern.hpp"
#include "ultramodern/config.hpp" #include "ultramodern/config.hpp"
#include "ultramodern/rt64_layer.hpp"
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) { extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
recomp::poll_inputs(); recomp::poll_inputs();
@ -59,17 +59,17 @@ extern "C" void recomp_get_target_framerate(uint8_t* rdram, recomp_context* ctx)
} }
extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) { extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config(); ultramodern::renderer::GraphicsConfig graphics_config = ultramodern::renderer::get_graphics_config();
float original = _arg<0, float>(rdram, ctx); float original = _arg<0, float>(rdram, ctx);
int width, height; int width, height;
recompui::get_window_size(width, height); recompui::get_window_size(width, height);
switch (graphics_config.ar_option) { switch (graphics_config.ar_option) {
case RT64::UserConfiguration::AspectRatio::Original: case ultramodern::renderer::AspectRatio::Original:
default: default:
_return(ctx, original); _return(ctx, original);
return; return;
case RT64::UserConfiguration::AspectRatio::Expand: case ultramodern::renderer::AspectRatio::Expand:
_return(ctx, std::max(static_cast<float>(width) / height, original)); _return(ctx, std::max(static_cast<float>(width) / height, original));
return; return;
} }
@ -104,7 +104,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) { extern "C" void recomp_high_precision_fb_enabled(uint8_t * rdram, recomp_context * ctx) {
_return(ctx, static_cast<s32>(ultramodern::RT64HighPrecisionFBEnabled())); _return(ctx, static_cast<s32>(zelda64::renderer::RT64HighPrecisionFBEnabled()));
} }
extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) { extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) {

View File

@ -24,6 +24,7 @@
#include "recomp_input.h" #include "recomp_input.h"
#include "zelda_config.h" #include "zelda_config.h"
#include "zelda_sound.h" #include "zelda_sound.h"
#include "zelda_render.h"
#include "ovl_patches.hpp" #include "ovl_patches.hpp"
#include "librecomp/game.hpp" #include "librecomp/game.hpp"
@ -40,9 +41,6 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "../../lib/rt64/src/contrib/stb/stb_image.h" #include "../../lib/rt64/src/contrib/stb/stb_image.h"
extern "C" void init();
/*extern "C"*/ void start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t* audio_callbacks, const ultramodern::input_callbacks_t* input_callbacks);
template<typename... Ts> template<typename... Ts>
void exit_error(const char* str, Ts ...args) { void exit_error(const char* str, Ts ...args) {
// TODO pop up an error // TODO pop up an error
@ -121,11 +119,11 @@ bool SetImageAsIcon(const char* filename, SDL_Window* window)
SDL_Window* window; SDL_Window* window;
ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) { ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, SDL_WINDOW_RESIZABLE ); window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, SDL_WINDOW_RESIZABLE );
#if defined(__linux__) #if defined(__linux__)
SetImageAsIcon("icons/512.png",window); SetImageAsIcon("icons/512.png",window);
if (ultramodern::get_graphics_config().wm_option == ultramodern::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux if (ultramodern::renderer::get_graphics_config().wm_option == ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux
SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP);
} else { } else {
SDL_SetWindowFullscreen(window,0); SDL_SetWindowFullscreen(window,0);
@ -141,7 +139,7 @@ ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t
SDL_GetWindowWMInfo(window, &wmInfo); SDL_GetWindowWMInfo(window, &wmInfo);
#if defined(_WIN32) #if defined(_WIN32)
return ultramodern::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() }; return ultramodern::renderer::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() };
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
static_assert(false && "Unimplemented"); static_assert(false && "Unimplemented");
#elif defined(__linux__) #elif defined(__linux__)
@ -149,7 +147,7 @@ ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t
exit_error("Unsupported SDL2 video driver \"%s\". Only X11 is supported on Linux.\n", SDL_GetCurrentVideoDriver()); exit_error("Unsupported SDL2 video driver \"%s\". Only X11 is supported on Linux.\n", SDL_GetCurrentVideoDriver());
} }
return ultramodern::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window }; return ultramodern::renderer::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window };
#else #else
static_assert(false && "Unimplemented"); static_assert(false && "Unimplemented");
#endif #endif
@ -390,6 +388,10 @@ int main(int argc, char** argv) {
.get_rsp_microcode = get_rsp_microcode, .get_rsp_microcode = get_rsp_microcode,
}; };
ultramodern::renderer::callbacks_t renderer_callbacks{
.create_render_context = zelda64::renderer::create_render_context,
};
ultramodern::gfx_callbacks_t gfx_callbacks{ ultramodern::gfx_callbacks_t gfx_callbacks{
.create_gfx = create_gfx, .create_gfx = create_gfx,
.create_window = create_window, .create_window = create_window,
@ -402,10 +404,11 @@ int main(int argc, char** argv) {
.set_frequency = set_frequency, .set_frequency = set_frequency,
}; };
ultramodern::input_callbacks_t input_callbacks{ ultramodern::input::callbacks_t input_callbacks{
.poll_input = recomp::poll_inputs, .poll_input = recomp::poll_inputs,
.get_input = recomp::get_n64_input, .get_input = recomp::get_n64_input,
.set_rumble = recomp::set_rumble, .set_rumble = recomp::set_rumble,
.get_connected_device_info = recomp::get_connected_device_info,
}; };
ultramodern::events::callbacks_t thread_callbacks{ ultramodern::events::callbacks_t thread_callbacks{
@ -417,7 +420,7 @@ int main(int argc, char** argv) {
.message_box = recompui::message_box, .message_box = recompui::message_box,
}; };
recomp::start({}, rsp_callbacks, audio_callbacks, input_callbacks, gfx_callbacks, thread_callbacks, error_handling_callbacks); recomp::start({}, rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, gfx_callbacks, thread_callbacks, error_handling_callbacks);
NFD_Quit(); NFD_Quit();

View File

@ -4,16 +4,16 @@
#include "librecomp/overlays.hpp" #include "librecomp/overlays.hpp"
void zelda64::register_overlays() { void zelda64::register_overlays() {
recomp::overlay_section_table_data_t sections { recomp::overlays::overlay_section_table_data_t sections {
.code_sections = section_table, .code_sections = section_table,
.num_code_sections = ARRLEN(section_table), .num_code_sections = ARRLEN(section_table),
.total_num_sections = num_sections, .total_num_sections = num_sections,
}; };
recomp::overlays_by_index_t overlays { recomp::overlays::overlays_by_index_t overlays {
.table = overlay_sections_by_index, .table = overlay_sections_by_index,
.len = ARRLEN(overlay_sections_by_index), .len = ARRLEN(overlay_sections_by_index),
}; };
recomp::register_overlays(sections, overlays); recomp::overlays::register_overlays(sections, overlays);
} }

View File

@ -6,7 +6,5 @@
#include "librecomp/game.hpp" #include "librecomp/game.hpp"
void zelda64::register_patches() { void zelda64::register_patches() {
// TODO: This is a bit awful, maybe provide only one functions that does both in librecomp? recomp::overlays::register_patches(mm_patches_bin, sizeof(mm_patches_bin), section_table);
recomp::register_patch(mm_patches_bin, sizeof(mm_patches_bin));
recomp::register_patch_section(section_table);
} }

View File

@ -0,0 +1,378 @@
#include <memory>
#include <cstring>
#define HLSL_CPU
#include "hle/rt64_application.h"
#include "rt64_render_hooks.h"
#include "ultramodern/ultramodern.hpp"
#include "ultramodern/config.hpp"
#include "zelda_render.h"
#include "recomp_ui.h"
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];
unsigned int MI_INTR_REG = 0;
unsigned int DPC_START_REG = 0;
unsigned int DPC_END_REG = 0;
unsigned int DPC_CURRENT_REG = 0;
unsigned int DPC_STATUS_REG = 0;
unsigned int DPC_CLOCK_REG = 0;
unsigned int DPC_BUFBUSY_REG = 0;
unsigned int DPC_PIPEBUSY_REG = 0;
unsigned int DPC_TMEM_REG = 0;
unsigned int VI_STATUS_REG = 0;
unsigned int VI_ORIGIN_REG = 0;
unsigned int VI_WIDTH_REG = 0;
unsigned int VI_INTR_REG = 0;
unsigned int VI_V_CURRENT_LINE_REG = 0;
unsigned int VI_TIMING_REG = 0;
unsigned int VI_V_SYNC_REG = 0;
unsigned int VI_H_SYNC_REG = 0;
unsigned int VI_LEAP_REG = 0;
unsigned int VI_H_START_REG = 0;
unsigned int VI_V_START_REG = 0;
unsigned int VI_V_BURST_REG = 0;
unsigned int VI_X_SCALE_REG = 0;
unsigned int VI_Y_SCALE_REG = 0;
void dummy_check_interrupts() {}
RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) {
if (bits & RT64::RenderSampleCount::Bits::COUNT_2) {
if (bits & RT64::RenderSampleCount::Bits::COUNT_4) {
if (bits & RT64::RenderSampleCount::Bits::COUNT_8) {
return RT64::UserConfiguration::Antialiasing::MSAA8X;
}
return RT64::UserConfiguration::Antialiasing::MSAA4X;
}
return RT64::UserConfiguration::Antialiasing::MSAA2X;
};
return RT64::UserConfiguration::Antialiasing::None;
}
RT64::UserConfiguration::AspectRatio to_rt64(ultramodern::renderer::AspectRatio option) {
switch (option) {
case ultramodern::renderer::AspectRatio::Original:
return RT64::UserConfiguration::AspectRatio::Original;
case ultramodern::renderer::AspectRatio::Expand:
return RT64::UserConfiguration::AspectRatio::Expand;
case ultramodern::renderer::AspectRatio::Manual:
return RT64::UserConfiguration::AspectRatio::Manual;
case ultramodern::renderer::AspectRatio::OptionCount:
return RT64::UserConfiguration::AspectRatio::OptionCount;
}
}
RT64::UserConfiguration::Antialiasing to_rt64(ultramodern::renderer::Antialiasing option) {
switch (option) {
case ultramodern::renderer::Antialiasing::None:
return RT64::UserConfiguration::Antialiasing::None;
case ultramodern::renderer::Antialiasing::MSAA2X:
return RT64::UserConfiguration::Antialiasing::MSAA2X;
case ultramodern::renderer::Antialiasing::MSAA4X:
return RT64::UserConfiguration::Antialiasing::MSAA4X;
case ultramodern::renderer::Antialiasing::MSAA8X:
return RT64::UserConfiguration::Antialiasing::MSAA8X;
case ultramodern::renderer::Antialiasing::OptionCount:
return RT64::UserConfiguration::Antialiasing::OptionCount;
}
}
RT64::UserConfiguration::RefreshRate to_rt64(ultramodern::renderer::RefreshRate option) {
switch (option) {
case ultramodern::renderer::RefreshRate::Original:
return RT64::UserConfiguration::RefreshRate::Original;
case ultramodern::renderer::RefreshRate::Display:
return RT64::UserConfiguration::RefreshRate::Display;
case ultramodern::renderer::RefreshRate::Manual:
return RT64::UserConfiguration::RefreshRate::Manual;
case ultramodern::renderer::RefreshRate::OptionCount:
return RT64::UserConfiguration::RefreshRate::OptionCount;
}
}
RT64::UserConfiguration::InternalColorFormat to_rt64(ultramodern::renderer::HighPrecisionFramebuffer option) {
switch (option) {
case ultramodern::renderer::HighPrecisionFramebuffer::Off:
return RT64::UserConfiguration::InternalColorFormat::Standard;
case ultramodern::renderer::HighPrecisionFramebuffer::On:
return RT64::UserConfiguration::InternalColorFormat::High;
case ultramodern::renderer::HighPrecisionFramebuffer::Auto:
return RT64::UserConfiguration::InternalColorFormat::Automatic;
case ultramodern::renderer::HighPrecisionFramebuffer::OptionCount:
return RT64::UserConfiguration::InternalColorFormat::OptionCount;
}
}
void set_application_user_config(RT64::Application* application, const ultramodern::renderer::GraphicsConfig& config) {
switch (config.res_option) {
default:
case ultramodern::renderer::Resolution::Auto:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::WindowIntegerScale;
application->userConfig.downsampleMultiplier = 1;
break;
case ultramodern::renderer::Resolution::Original:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
application->userConfig.resolutionMultiplier = std::max(config.ds_option, 1);
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
break;
case ultramodern::renderer::Resolution::Original2x:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
application->userConfig.resolutionMultiplier = 2.0 * std::max(config.ds_option, 1);
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
break;
}
switch (config.hr_option) {
default:
case ultramodern::renderer::HUDRatioMode::Original:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Original;
break;
case ultramodern::renderer::HUDRatioMode::Clamp16x9:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Manual;
application->userConfig.extAspectTarget = 16.0/9.0;
break;
case ultramodern::renderer::HUDRatioMode::Full:
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Expand;
break;
}
application->userConfig.aspectRatio = to_rt64(config.ar_option);
application->userConfig.antialiasing = to_rt64(config.msaa_option);
application->userConfig.refreshRate = to_rt64(config.rr_option);
application->userConfig.refreshRateTarget = config.rr_manual_value;
application->userConfig.internalColorFormat = to_rt64(config.hpfb_option);
}
ultramodern::renderer::SetupResult map_setup_result(RT64::Application::SetupResult rt64_result) {
switch (rt64_result) {
case RT64::Application::SetupResult::Success:
return ultramodern::renderer::SetupResult::Success;
case RT64::Application::SetupResult::DynamicLibrariesNotFound:
return ultramodern::renderer::SetupResult::DynamicLibrariesNotFound;
case RT64::Application::SetupResult::InvalidGraphicsAPI:
return ultramodern::renderer::SetupResult::InvalidGraphicsAPI;
case RT64::Application::SetupResult::GraphicsAPINotFound:
return ultramodern::renderer::SetupResult::GraphicsAPINotFound;
case RT64::Application::SetupResult::GraphicsDeviceNotFound:
return ultramodern::renderer::SetupResult::GraphicsDeviceNotFound;
}
fprintf(stderr, "Unhandled `RT64::Application::SetupResult` ?\n");
assert(false);
std::exit(EXIT_FAILURE);
}
zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool debug) {
static unsigned char dummy_rom_header[0x40];
recompui::set_render_hooks();
// Set up the RT64 application core fields.
RT64::Application::Core appCore{};
#if defined(_WIN32)
appCore.window = window_handle.window;
#elif defined(__ANDROID__)
assert(false && "Unimplemented");
#elif defined(__linux__)
appCore.window.display = window_handle.display;
appCore.window.window = window_handle.window;
#elif defined(__APPLE__)
appCore.window.window = window_handle.window;
appCore.window.view = window_handle.view;
#endif
appCore.checkInterrupts = dummy_check_interrupts;
appCore.HEADER = dummy_rom_header;
appCore.RDRAM = rdram;
appCore.DMEM = DMEM;
appCore.IMEM = IMEM;
appCore.MI_INTR_REG = &MI_INTR_REG;
appCore.DPC_START_REG = &DPC_START_REG;
appCore.DPC_END_REG = &DPC_END_REG;
appCore.DPC_CURRENT_REG = &DPC_CURRENT_REG;
appCore.DPC_STATUS_REG = &DPC_STATUS_REG;
appCore.DPC_CLOCK_REG = &DPC_CLOCK_REG;
appCore.DPC_BUFBUSY_REG = &DPC_BUFBUSY_REG;
appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
appCore.DPC_TMEM_REG = &DPC_TMEM_REG;
appCore.VI_STATUS_REG = &VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &VI_WIDTH_REG;
appCore.VI_INTR_REG = &VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &VI_LEAP_REG;
appCore.VI_H_START_REG = &VI_H_START_REG;
appCore.VI_V_START_REG = &VI_V_START_REG;
appCore.VI_V_BURST_REG = &VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
// Set up the RT64 application configuration fields.
RT64::ApplicationConfiguration appConfig;
appConfig.useConfigurationFile = false;
// Create the RT64 application.
app = std::make_unique<RT64::Application>(appCore, appConfig);
// Set initial user config settings based on the current settings.
auto& cur_config = ultramodern::renderer::get_graphics_config();
set_application_user_config(app.get(), cur_config);
app->userConfig.developerMode = debug;
// Force gbi depth branches to prevent LODs from kicking in.
app->enhancementConfig.f3dex.forceBranch = true;
// Scale LODs based on the output resolution.
app->enhancementConfig.textureLOD.scale = true;
// Pick an API if the user has set an override.
switch (cur_config.api_option) {
case ultramodern::renderer::GraphicsApi::D3D12:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::D3D12;
break;
case ultramodern::renderer::GraphicsApi::Vulkan:
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Vulkan;
break;
default:
case ultramodern::renderer::GraphicsApi::Auto:
// Don't override if auto is selected.
break;
}
// Set up the RT64 application.
uint32_t thread_id = 0;
#ifdef _WIN32
thread_id = window_handle.thread_id;
#endif
setup_result = map_setup_result(app->setup(thread_id));
if (setup_result != ultramodern::renderer::SetupResult::Success) {
app = nullptr;
return;
}
// Set the application's fullscreen state.
app->setFullScreen(cur_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
// Check if the selected device actually supports MSAA sample positions and MSAA for for the formats that will be used
// and downgrade the configuration accordingly.
if (app->device->getCapabilities().sampleLocations) {
RT64::RenderSampleCounts color_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::R8G8B8A8_UNORM);
RT64::RenderSampleCounts depth_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::D32_FLOAT);
RT64::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts;
device_max_msaa = compute_max_supported_aa(common_sample_counts);
sample_positions_supported = true;
}
else {
device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
sample_positions_supported = false;
}
high_precision_fb_enabled = app->shaderLibrary->usesHDR;
}
zelda64::renderer::RT64Context::~RT64Context() = default;
void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
app->state->rsp->reset();
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
}
void zelda64::renderer::RT64Context::update_screen(uint32_t vi_origin) {
VI_ORIGIN_REG = vi_origin;
app->updateScreen();
}
void zelda64::renderer::RT64Context::shutdown() {
if (app != nullptr) {
app->end();
}
}
bool zelda64::renderer::RT64Context::update_config(const ultramodern::renderer::GraphicsConfig& old_config, const ultramodern::renderer::GraphicsConfig& new_config) {
if (old_config == new_config) {
return false;
}
if (new_config.wm_option != old_config.wm_option) {
app->setFullScreen(new_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
}
set_application_user_config(app.get(), new_config);
app->updateUserConfig(true);
if (new_config.msaa_option != old_config.msaa_option) {
app->updateMultisampling();
}
return true;
}
void zelda64::renderer::RT64Context::enable_instant_present() {
// Enable the present early presentation mode for minimal latency.
app->enhancementConfig.presentation.mode = RT64::EnhancementConfiguration::Presentation::Mode::PresentEarly;
app->updateEnhancementConfig();
}
uint32_t zelda64::renderer::RT64Context::get_display_framerate() const {
return app->presentQueue->ext.sharedResources->swapChainRate;
}
float zelda64::renderer::RT64Context::get_resolution_scale() const {
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 zelda64::renderer::RT64Context::load_shader_cache(std::span<const char> cache_binary) {
// TODO figure out how to avoid a copy here.
std::istringstream cache_stream{std::string{cache_binary.data(), cache_binary.size()}};
if (!app->rasterShaderCache->loadOfflineList(cache_stream)) {
printf("Failed to preload shader cache!\n");
assert(false);
}
}
RT64::UserConfiguration::Antialiasing zelda64::renderer::RT64MaxMSAA() {
return device_max_msaa;
}
std::unique_ptr<ultramodern::renderer::RendererContext> zelda64::renderer::create_render_context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode) {
return std::make_unique<zelda64::renderer::RT64Context>(rdram, window_handle, developer_mode);
}
bool zelda64::renderer::RT64SamplePositionsSupported() {
return sample_positions_supported;
}
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
return high_precision_fb_enabled;
}

View File

@ -3,13 +3,13 @@
#include "zelda_sound.h" #include "zelda_sound.h"
#include "zelda_config.h" #include "zelda_config.h"
#include "zelda_debug.h" #include "zelda_debug.h"
#include "zelda_render.h"
#include "promptfont.h" #include "promptfont.h"
#include "ultramodern/config.hpp" #include "ultramodern/config.hpp"
#include "ultramodern/ultramodern.hpp" #include "ultramodern/ultramodern.hpp"
#include "RmlUi/Core.h" #include "RmlUi/Core.h"
#include "ultramodern/rt64_layer.hpp"
ultramodern::GraphicsConfig new_options; ultramodern::renderer::GraphicsConfig new_options;
Rml::DataModelHandle nav_help_model_handle; Rml::DataModelHandle nav_help_model_handle;
Rml::DataModelHandle general_model_handle; Rml::DataModelHandle general_model_handle;
Rml::DataModelHandle controls_model_handle; Rml::DataModelHandle controls_model_handle;
@ -214,9 +214,9 @@ extern SDL_Window* window;
#endif #endif
void apply_graphics_config(void) { void apply_graphics_config(void) {
ultramodern::set_graphics_config(new_options); ultramodern::renderer::set_graphics_config(new_options);
#if defined(__linux__) // TODO: Remove once RT64 gets native fullscreen support on Linux #if defined(__linux__) // TODO: Remove once RT64 gets native fullscreen support on Linux
if (new_options.wm_option == ultramodern::WindowMode::Fullscreen) { if (new_options.wm_option == ultramodern::renderer::WindowMode::Fullscreen) {
SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP);
} else { } else {
SDL_SetWindowFullscreen(window,0); SDL_SetWindowFullscreen(window,0);
@ -225,7 +225,7 @@ void apply_graphics_config(void) {
} }
void close_config_menu() { void close_config_menu() {
if (ultramodern::get_graphics_config() != new_options) { if (ultramodern::renderer::get_graphics_config() != new_options) {
prompt_context.open_prompt( prompt_context.open_prompt(
"Graphics options have changed", "Graphics options have changed",
"Would you like to apply or discard the changes?", "Would you like to apply or discard the changes?",
@ -237,7 +237,7 @@ void close_config_menu() {
close_config_menu_impl(); close_config_menu_impl();
}, },
[]() { []() {
new_options = ultramodern::get_graphics_config(); new_options = ultramodern::renderer::get_graphics_config();
graphics_model_handle.DirtyAllVariables(); graphics_model_handle.DirtyAllVariables();
close_config_menu_impl(); close_config_menu_impl();
}, },
@ -612,7 +612,7 @@ public:
} }
ultramodern::sleep_milliseconds(50); ultramodern::sleep_milliseconds(50);
new_options = ultramodern::get_graphics_config(); new_options = ultramodern::renderer::get_graphics_config();
bind_config_list_events(constructor); bind_config_list_events(constructor);
constructor.BindFunc("res_option", constructor.BindFunc("res_option",
@ -639,7 +639,7 @@ public:
}); });
constructor.BindFunc("ds_option", constructor.BindFunc("ds_option",
[](Rml::Variant& out) { [](Rml::Variant& out) {
if (new_options.res_option == ultramodern::Resolution::Auto) { if (new_options.res_option == ultramodern::renderer::Resolution::Auto) {
out = 1; out = 1;
} else { } else {
out = new_options.ds_option; out = new_options.ds_option;
@ -658,23 +658,23 @@ public:
constructor.BindFunc("options_changed", constructor.BindFunc("options_changed",
[](Rml::Variant& out) { [](Rml::Variant& out) {
out = (ultramodern::get_graphics_config() != new_options); out = (ultramodern::renderer::get_graphics_config() != new_options);
}); });
constructor.BindFunc("ds_info", constructor.BindFunc("ds_info",
[](Rml::Variant& out) { [](Rml::Variant& out) {
switch (new_options.res_option) { switch (new_options.res_option) {
default: default:
case ultramodern::Resolution::Auto: case ultramodern::renderer::Resolution::Auto:
out = "Downsampling is not available at auto resolution"; out = "Downsampling is not available at auto resolution";
return; return;
case ultramodern::Resolution::Original: case ultramodern::renderer::Resolution::Original:
if (new_options.ds_option == 2) { if (new_options.ds_option == 2) {
out = "Rendered in 480p and scaled to 240p"; out = "Rendered in 480p and scaled to 240p";
} else if (new_options.ds_option == 4) { } else if (new_options.ds_option == 4) {
out = "Rendered in 960p and scaled to 240p"; out = "Rendered in 960p and scaled to 240p";
} }
return; return;
case ultramodern::Resolution::Original2x: case ultramodern::renderer::Resolution::Original2x:
if (new_options.ds_option == 2) { if (new_options.ds_option == 2) {
out = "Rendered in 960p and scaled to 480p"; out = "Rendered in 960p and scaled to 480p";
} else if (new_options.ds_option == 4) { } else if (new_options.ds_option == 4) {
@ -973,7 +973,7 @@ public:
void make_bindings(Rml::Context* context) override { void make_bindings(Rml::Context* context) override {
// initially set cont state for ui help // initially set cont state for ui help
recomp::config_menu_set_cont_or_kb(recompui::get_cont_active()); recomp::config_menu_set_cont_or_kb(recompui::get_cont_active());
make_nav_help_bindings(context); make_nav_help_bindings(context);
make_general_bindings(context); make_general_bindings(context);
make_controls_bindings(context); make_controls_bindings(context);
@ -1000,18 +1000,18 @@ void zelda64::set_debug_mode_enabled(bool enabled) {
} }
void recompui::update_supported_options() { void recompui::update_supported_options() {
msaa2x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA2X; msaa2x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA2X;
msaa4x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA4X; msaa4x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA4X;
msaa8x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA8X; msaa8x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA8X;
sample_positions_supported = ultramodern::RT64SamplePositionsSupported(); sample_positions_supported = zelda64::renderer::RT64SamplePositionsSupported();
new_options = ultramodern::get_graphics_config(); new_options = ultramodern::renderer::get_graphics_config();
graphics_model_handle.DirtyAllVariables(); graphics_model_handle.DirtyAllVariables();
} }
void recompui::toggle_fullscreen() { void recompui::toggle_fullscreen() {
new_options.wm_option = (new_options.wm_option == ultramodern::WindowMode::Windowed) ? ultramodern::WindowMode::Fullscreen : ultramodern::WindowMode::Windowed; new_options.wm_option = (new_options.wm_option == ultramodern::renderer::WindowMode::Windowed) ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed;
apply_graphics_config(); apply_graphics_config();
graphics_model_handle.DirtyVariable("wm_option"); graphics_model_handle.DirtyVariable("wm_option");
} }

View File

@ -19,7 +19,6 @@
#include "concurrentqueue.h" #include "concurrentqueue.h"
#include "ultramodern/rt64_layer.hpp"
#include "rt64_render_hooks.h" #include "rt64_render_hooks.h"
#include "rt64_render_interface_builders.h" #include "rt64_render_interface_builders.h"
@ -1458,7 +1457,7 @@ void deinit_hook() {
ui_context.reset(); ui_context.reset();
} }
void set_rt64_hooks() { void recompui::set_render_hooks() {
RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook); RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook);
} }