Hooked up graphics option menu to RT64, updated RT64

This commit is contained in:
Mr-Wiseguy 2024-01-01 00:09:14 -05:00
parent ac131c7835
commit 09bacbe82c
12 changed files with 354 additions and 135 deletions

View File

@ -127,7 +127,9 @@ target_include_directories(MMRecomp PRIVATE
${CMAKE_SOURCE_DIR}/lib/concurrentqueue ${CMAKE_SOURCE_DIR}/lib/concurrentqueue
${CMAKE_SOURCE_DIR}/lib/RmlUi/Include ${CMAKE_SOURCE_DIR}/lib/RmlUi/Include
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends ${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/contrib
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/contrib/mupen64plus-win32-deps/SDL2-2.26.3/include ${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/contrib/mupen64plus-win32-deps/SDL2-2.26.3/include
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/contrib/hlslpp/include
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src ${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/rhi ${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/rhi
${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/render ${CMAKE_SOURCE_DIR}/lib/RT64-HLE/src/render

View File

@ -33,55 +33,55 @@
<div style="flex:1 1 auto"> <div style="flex:1 1 auto">
<tabset> <tabset>
<tab id="tab_graphics" autofocus>Graphics</tab> <tab id="tab_graphics" autofocus>Graphics</tab>
<panel> <panel data-model="graphics_model">
<form> <form>
<div class="option_container"> <div class="option_container">
<div class="option_row"> <div class="option_row">
<div class="option"> <div class="option">
<label style="padding:4dp;">Resolution</label><br/> <label class="option_title">Resolution</label><br/>
<hr/> <hr/>
<div class="option_list"> <div class="option_list">
<input type="radio" name="resolution" id="res_original"/> <input type="radio" name="resolution" data-checked="res_option" value="Original" id="res_original"/>
<label for="res_original">Original</label> <label for="res_original">Original</label>
<input type="radio" name="resolution" id="res_2x" style="nav-down:#ar_expand"/> <input type="radio" name="resolution" data-checked="res_option" value="Original2x" id="res_2x" style="nav-down:#ar_expand"/>
<label for="res_2x">Original 2x</label> <label for="res_2x">Original 2x</label>
<input type="radio" name="resolution" id="res_auto" style="nav-down:#ar_expand" checked="checked"/> <input type="radio" name="resolution" data-checked="res_option" value="Auto" id="res_auto" style="nav-down:#ar_expand"/>
<label for="res_auto">Auto</label> <label for="res_auto">Auto</label>
</div> </div>
</div> </div>
<div class="option"> <div class="option">
<label style="padding:4dp;">Window Mode</label><br/> <label class="option_title">Window Mode</label><br/>
<hr/> <hr/>
<div class="option_list"> <div class="option_list">
<input type="radio" name="windowmode" id="wm_windowed" style="nav-down:#msaa_none" checked="checked"/> <input type="radio" name="windowmode" data-checked="wm_option" value="Windowed" id="wm_windowed" style="nav-down:#msaa_none"/>
<label for="wm_windowed">Windowed</label> <label for="wm_windowed">Windowed</label>
<input type="radio" name="windowmode" id="wm_fullscreen" style="nav-right:none"/> <input type="radio" name="windowmode" data-checked="wm_option" value="Fullscreen" id="wm_fullscreen" style="nav-right:none"/>
<label for="wm_fullscreen">Fullscreen</label> <label for="wm_fullscreen">Fullscreen</label>
</div> </div>
</div> </div>
</div> </div>
<div class="option_row"> <div class="option_row">
<div class="option"> <div class="option">
<label style="padding:4dp;">Aspect Ratio</label><br/> <label class="option_title">Aspect Ratio</label><br/>
<hr/> <hr/>
<div class="option_list"> <div class="option_list">
<input type="radio" name="aspectratio" id="ar_original" style="nav-left:none"/> <input type="radio" name="aspectratio" data-checked="ar_option" value="Original" id="ar_original" style="nav-left:none"/>
<label for="ar_original">Original</label> <label for="ar_original">Original</label>
<input type="radio" name="aspectratio" id="ar_expand" style="nav-up:#res_2x" checked="checked"/> <input type="radio" name="aspectratio" data-checked="ar_option" value="Expand" id="ar_expand" style="nav-up:#res_2x"/>
<label for="ar_expand">Expand</label> <label for="ar_expand">Expand</label>
</div> </div>
</div> </div>
<div class="option"> <div class="option">
<label style="padding:4dp;">MS Anti-Aliasing</label><br/> <label class="option_title">MS Anti-Aliasing</label><br/>
<hr/> <hr/>
<div class="option_list"> <div class="option_list">
<input type="radio" name="antialiasing" id="msaa_none" checked="checked" style="nav-up:#wm_windowed;nav-left:none"/> <input type="radio" name="antialiasing" data-checked="msaa_option" value="None" id="msaa_none" style="nav-up:#wm_windowed;nav-down:#rr_manual"/>
<label for="msaa_none">None</label> <label for="msaa_none">None</label>
<input type="radio" name="antialiasing" id="msaa_2x"/> <input type="radio" name="antialiasing" data-checked="msaa_option" value="MSAA2X" id="msaa_2x" style="nav-down:#rr_manual"/>
<label for="msaa_2x">2x</label> <label for="msaa_2x">2x</label>
<input type="radio" name="antialiasing" id="msaa_4x"/> <input type="radio" name="antialiasing" data-checked="msaa_option" value="MSAA4X" id="msaa_4x" style="nav-down:#rr_manual"/>
<label for="msaa_4x">4x</label> <label for="msaa_4x">4x</label>
<input type="radio" name="antialiasing" id="msaa_8x" style="nav-right:none"/> <input type="radio" name="antialiasing" data-checked="msaa_option" value="MSAA8X" id="msaa_8x" style="nav-right:none;nav-down:#rr_manual"/>
<label for="msaa_8x">8x</label> <label for="msaa_8x">8x</label>
</div> </div>
</div> </div>
@ -89,20 +89,24 @@
<div class="option_row"> <div class="option_row">
<div class="option"> <div class="option">
<label style="padding:4dp;">Refresh Rate</label><br/> <label class="option_title">Framerate</label><br/>
<hr/> <hr/>
<div class="option_list" style="flex-wrap: wrap;"> <div class="option_list">
<input type="radio" name="refreshrate" id="rr_original" style="nav-up:#ar_expand"/> <input type="radio" name="refreshrate" data-checked="rr_option" value="Original" id="rr_original" style="nav-up:#ar_expand"/>
<label for="rr_original">Original</label> <label for="rr_original">Original</label>
<input type="radio" name="refreshrate" id="rr_display" style="nav-up:#ar_expand"/> <input type="radio" name="refreshrate" data-checked="rr_option" value="Display" id="rr_display" style="nav-up:#ar_expand"/>
<label for="rr_display">Display</label> <label for="rr_display">Display</label>
<input type="radio" name="refreshrate" id="rr_manual" checked="checked" style="nav-up:#msaa_none;nav-right:none"/> <input type="radio" name="refreshrate" data-checked="rr_option" value="Manual" id="rr_manual" style="nav-up:#msaa_none;nav-right:none"/>
<label for="rr_manual">Manual</label> <label for="rr_manual">Manual</label>
<div style="flex-basis:100%;height:0"/> </div>
<input id="rr_manual_input" type="range" min="60" max="1000" style="font:normal;flex:1;nav-up:auto;nav-down:auto;width:100%" value="60"/> <div data-if="rr_option=='Manual'" class="option_list" style="padding:10dp">
<input id="rr_manual_input" type="range" min="20" max="1000" style="flex:1;margin-left:30dp;margin-right:30dp;nav-up:auto;nav-down:auto;" data-value="rr_manual_value"/>
<label style="flex:0 0 50dp">{{rr_manual_value}}</label>
</div> </div>
</div> </div>
</div> </div>
<button nav-return="rr_manual" style="margin-top:10dp;margin-left:auto;margin-right:auto;width:100dp" data-attrif-disabled="!options_changed" onclick="apply_options">Apply</button>
</div> </div>
</form> </form>
</panel> </panel>

View File

@ -33,7 +33,7 @@ div.option_container {
margin-left:auto; margin-left:auto;
margin-right:auto; margin-right:auto;
font-effect: outline(2dp black); font-effect: outline(2dp black);
font-size:24dp; font-size:20dp;
background:rgba(50,50,50,200); background:rgba(50,50,50,200);
} }
@ -55,6 +55,11 @@ div.option_list {
justify-content:center; justify-content:center;
} }
label.option_title {
padding:4dp;
font-size:24dp;
}
div#title_bar { div#title_bar {
z-index: 1; z-index: 1;
position: absolute; position: absolute;
@ -137,17 +142,18 @@ input.submit {
/* vertical-align: center; */ /* vertical-align: center; */
height: auto; height: auto;
width: 100%; width: 100%;
focus:auto;
tab-index:auto;
nav-up:auto;
nav-down:auto;
nav-right:auto;
nav-left:auto;
} }
button:focus, button:focus,
input.submit:focus { input.submit:focus {
font-effect: blur(3dp #fff); /* font-effect: blur(3dp #fff); */
background-color: rgb(120, 120, 120); color: #329696;
}
button:hover,
input.submit:hover {
background-color: rgb(150, 150, 150);
} }
button:active, button:active,
@ -161,6 +167,34 @@ input.submit:disabled {
cursor: unavailable cursor: unavailable
} }
button[disabled] {
/* decorator: image(button); */
/* image-color: #329696; */
/* color:black; */
color:rgb(100,100,100);
background-color: rgb(50, 50, 50);
/* focus:none;
tab-index:none;
nav-up:none;
nav-down:none;
nav-right:none;
nav-left:none; */
}
button:focus[disabled] {
/* decorator: image(button); */
/* image-color: #329696; */
/* color:black; */
color:#329696;
background-color: rgb(50, 50, 50);
/* focus:none;
tab-index:none;
nav-up:none;
nav-down:none;
nav-right:none;
nav-left:none; */
}
input.text, input.text,
input.password { input.password {
box-sizing: border-box; box-sizing: border-box;
@ -316,14 +350,6 @@ input.radio + label {
/* decorator: image(radio) */ /* decorator: image(radio) */
} }
#rr_manual:not(:checked) ~ #rr_manual_input {
/* background: rgb(120,120,120); */
/* font-effect: outline(2dp black); */
display:none;
/* display:none; */
/* decorator: image(radio) */
}
input.radio:checked + label { input.radio:checked + label {
/* background: rgb(72, 102, 255); */ /* background: rgb(72, 102, 255); */
color: white; color: white;
@ -403,12 +429,6 @@ input.checkbox:checked:active {
decorator: image(checkbox-checked-active) decorator: image(checkbox-checked-active)
} */ } */
input.range {
height: 32dp;
margin:auto;
width:80%;
}
input.range slidertrack { input.range slidertrack {
margin-top: 7dp; margin-top: 7dp;
height: 6dp; height: 6dp;

View File

@ -8,6 +8,7 @@
namespace Rml { namespace Rml {
class ElementDocument; class ElementDocument;
class EventListenerInstancer; class EventListenerInstancer;
class Context;
} }
namespace recomp { namespace recomp {
@ -16,6 +17,7 @@ namespace recomp {
bool try_deque_event(SDL_Event& out); bool try_deque_event(SDL_Event& out);
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer(); std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer();
void make_ui_bindings(Rml::Context* context);
enum class Menu { enum class Menu {
Launcher, Launcher,

View File

@ -2,81 +2,22 @@
#define __RT64_LAYER_H__ #define __RT64_LAYER_H__
#include "../ultramodern/ultramodern.hpp" #include "../ultramodern/ultramodern.hpp"
#include "../ultramodern/config.hpp"
typedef struct { namespace RT64 {
// void* hWnd; struct Application;
// void* hStatusBar; }
// int Reserved; namespace ultramodern {
struct WindowHandle;
}
unsigned char* HEADER; /* This is the rom header (first 40h bytes of the rom) */ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle);
unsigned char* RDRAM; void RT64UpdateConfig(RT64::Application* application, const ultramodern::GraphicsConfig& old_config, const ultramodern::GraphicsConfig& new_config);
unsigned char* DMEM; void RT64SendDL(uint8_t* rdram, const OSTask* task);
unsigned char* IMEM; void RT64UpdateScreen(uint32_t vi_origin);
void RT64ChangeWindow();
unsigned int* MI_INTR_REG; void RT64Shutdown();
unsigned int* DPC_START_REG;
unsigned int* DPC_END_REG;
unsigned int* DPC_CURRENT_REG;
unsigned int* DPC_STATUS_REG;
unsigned int* DPC_CLOCK_REG;
unsigned int* DPC_BUFBUSY_REG;
unsigned int* DPC_PIPEBUSY_REG;
unsigned int* DPC_TMEM_REG;
unsigned int* VI_STATUS_REG;
unsigned int* VI_ORIGIN_REG;
unsigned int* VI_WIDTH_REG;
unsigned int* VI_INTR_REG;
unsigned int* VI_V_CURRENT_LINE_REG;
unsigned int* VI_TIMING_REG;
unsigned int* VI_V_SYNC_REG;
unsigned int* VI_H_SYNC_REG;
unsigned int* VI_LEAP_REG;
unsigned int* VI_H_START_REG;
unsigned int* VI_V_START_REG;
unsigned int* VI_V_BURST_REG;
unsigned int* VI_X_SCALE_REG;
unsigned int* VI_Y_SCALE_REG;
void (*CheckInterrupts)(void);
unsigned int version;
unsigned int* SP_STATUS_REG;
const unsigned int* RDRAM_SIZE;
} GFX_INFO;
#ifdef _WIN32
#define DLLEXPORT extern "C" __declspec(dllexport)
#define DLLIMPORT extern "C" __declspec(dllimport)
#define CALL __cdecl
#else
#define DLLEXPORT extern "C" __attribute__((visibility("default")))
#define DLLIMPORT extern "C"
#endif
// Dynamic loading
//DLLEXPORT int (CALL *InitiateGFX)(GFX_INFO Gfx_Info) = nullptr;
//DLLEXPORT void (CALL *ProcessRDPList)(void) = nullptr;
//DLLEXPORT void (CALL *ProcessDList)(void) = nullptr;
//DLLEXPORT void (CALL *UpdateScreen)(void) = nullptr;
//DLLEXPORT void (CALL *PumpEvents)(void) = nullptr;
#if defined(_WIN32)
extern "C" int InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd, DWORD threadId);
#elif defined(__ANDROID__)
static_assert(false && "Unimplemented");
#elif defined(__linux__)
extern "C" int InitiateGFXLinux(GFX_INFO Gfx_Info, Window window, Display *display);
#else
static_assert(false && "Unimplemented");
#endif
DLLIMPORT void ProcessRDPList(void);
DLLIMPORT void ProcessDList(void);
DLLIMPORT void UpdateScreen(void);
DLLIMPORT void ChangeWindow(void);
DLLIMPORT void PluginShutdown(void);
void set_rt64_hooks(); void set_rt64_hooks();

@ -1 +1 @@
Subproject commit 12bf0efad21fb7cc1f27a630ae6dbcb90ddd1433 Subproject commit f57eeec44a49cf4fb3ffe6c22d9f0c3d5410fb72

View File

@ -2,10 +2,80 @@
#include <cstring> #include <cstring>
// #include <Windows.h> // #include <Windows.h>
#include "../ultramodern/ultramodern.hpp" #define HLSL_CPU
#include "hle/rt64_application.h"
#include "rt64_layer.h" #include "rt64_layer.h"
#include "rt64_render_hooks.h" #include "rt64_render_hooks.h"
typedef struct {
// void* hWnd;
// void* hStatusBar;
// int Reserved;
unsigned char* HEADER; /* This is the rom header (first 40h bytes of the rom) */
unsigned char* RDRAM;
unsigned char* DMEM;
unsigned char* IMEM;
unsigned int* MI_INTR_REG;
unsigned int* DPC_START_REG;
unsigned int* DPC_END_REG;
unsigned int* DPC_CURRENT_REG;
unsigned int* DPC_STATUS_REG;
unsigned int* DPC_CLOCK_REG;
unsigned int* DPC_BUFBUSY_REG;
unsigned int* DPC_PIPEBUSY_REG;
unsigned int* DPC_TMEM_REG;
unsigned int* VI_STATUS_REG;
unsigned int* VI_ORIGIN_REG;
unsigned int* VI_WIDTH_REG;
unsigned int* VI_INTR_REG;
unsigned int* VI_V_CURRENT_LINE_REG;
unsigned int* VI_TIMING_REG;
unsigned int* VI_V_SYNC_REG;
unsigned int* VI_H_SYNC_REG;
unsigned int* VI_LEAP_REG;
unsigned int* VI_H_START_REG;
unsigned int* VI_V_START_REG;
unsigned int* VI_V_BURST_REG;
unsigned int* VI_X_SCALE_REG;
unsigned int* VI_Y_SCALE_REG;
void (*CheckInterrupts)(void);
unsigned int version;
unsigned int* SP_STATUS_REG;
const unsigned int* RDRAM_SIZE;
} GFX_INFO;
#ifdef _WIN32
#define DLLEXPORT extern "C" __declspec(dllexport)
#define DLLIMPORT extern "C" __declspec(dllimport)
#define CALL __cdecl
#else
#define DLLEXPORT extern "C" __attribute__((visibility("default")))
#define DLLIMPORT extern "C"
#endif
#if defined(_WIN32)
extern "C" RT64::Application* InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd, DWORD threadId);
#elif defined(__ANDROID__)
static_assert(false && "Unimplemented");
#elif defined(__linux__)
extern "C" RT64::Application* InitiateGFXLinux(GFX_INFO Gfx_Info, Window window, Display *display);
#else
static_assert(false && "Unimplemented");
#endif
DLLIMPORT void ProcessRDPList(void);
DLLIMPORT void ProcessDList(void);
DLLIMPORT void UpdateScreen(void);
DLLIMPORT void ChangeWindow(void);
DLLIMPORT void PluginShutdown(void);
static uint8_t DMEM[0x1000]; static uint8_t DMEM[0x1000];
static uint8_t IMEM[0x1000]; static uint8_t IMEM[0x1000];
@ -45,7 +115,7 @@ void dummy_check_interrupts() {
} }
bool RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) { RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) {
set_rt64_hooks(); set_rt64_hooks();
// Dynamic loading // Dynamic loading
//auto RT64 = LoadLibrary("RT64.dll"); //auto RT64 = LoadLibrary("RT64.dll");
@ -134,3 +204,34 @@ void RT64ChangeWindow() {
void RT64Shutdown() { void RT64Shutdown() {
PluginShutdown(); PluginShutdown();
} }
void RT64UpdateConfig(RT64::Application* application, const ultramodern::GraphicsConfig& old_config, const ultramodern::GraphicsConfig& new_config) {
if (new_config.wm_option != old_config.wm_option) {
application->setFullScreen(new_config.wm_option == ultramodern::WindowMode::Fullscreen);
}
switch (new_config.res_option) {
default:
case ultramodern::Resolution::Auto:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::WindowIntegerScale;
break;
case ultramodern::Resolution::Original:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Original;
break;
case ultramodern::Resolution::Original2x:
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
application->userConfig.resolutionMultiplier = 2.0;
break;
}
application->userConfig.aspectRatio = new_config.ar_option;
application->userConfig.antialiasing = new_config.msaa_option;
application->userConfig.refreshRate = new_config.rr_option;
application->userConfig.refreshRateTarget = new_config.rr_manual_value;
application->updateUserConfig(true);
if (new_config.msaa_option != old_config.msaa_option) {
application->updateMultisampling();
}
}

View File

@ -2,6 +2,8 @@
#include "recomp_ui.h" #include "recomp_ui.h"
#include "../../ultramodern/ultramodern.hpp" #include "../../ultramodern/ultramodern.hpp"
#include "../../ultramodern/config.hpp"
#include "common/rt64_user_configuration.h"
#include "nfd.h" #include "nfd.h"
#include "RmlUi/Core.h" #include "RmlUi/Core.h"
@ -36,15 +38,97 @@ public:
} }
}; };
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, {
{ultramodern::Resolution::Original, "Original"},
{ultramodern::Resolution::Original2x, "Original2x"},
{ultramodern::Resolution::Auto, "Auto"},
});
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::WindowMode, {
{ultramodern::WindowMode::Windowed, "Windowed"},
{ultramodern::WindowMode::Fullscreen, "Fullscreen"}
});
ultramodern::GraphicsConfig cur_options;
ultramodern::GraphicsConfig new_options;
Rml::DataModelHandle options_handle;
template <typename T>
void get_option(const T& input, Rml::Variant& output) {
std::string value = "";
to_json(value, input);
if (value.empty()) {
throw std::runtime_error("Invalid value :" + std::to_string(int(input)));
}
output = value;
}
template <typename T>
void set_option(T& output, const Rml::Variant& input) {
T value = T::OptionCount;
from_json(input.Get<std::string>(), value);
if (value == T::OptionCount) {
throw std::runtime_error("Invalid value :" + input.Get<std::string>());
}
output = value;
}
template <typename T>
void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) {
constructor.BindFunc(name,
[option](Rml::Variant& out) { get_option(*option, out); },
[option](const Rml::Variant& in) { set_option(*option, in); options_handle.DirtyVariable("options_changed"); }
);
};
void recomp::make_ui_bindings(Rml::Context* context) {
Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model");
if (!constructor) {
throw std::runtime_error("Failed to make RmlUi data model constructor");
}
bind_option(constructor, "res_option", &new_options.res_option);
bind_option(constructor, "wm_option", &new_options.wm_option);
bind_option(constructor, "ar_option", &new_options.ar_option);
bind_option(constructor, "msaa_option", &new_options.msaa_option);
bind_option(constructor, "rr_option", &new_options.rr_option);
constructor.BindFunc("rr_manual_value",
[](Rml::Variant& out) {
out = new_options.rr_manual_value;
},
[](const Rml::Variant& in) {
new_options.rr_manual_value = in.Get<int>();
options_handle.DirtyVariable("options_changed");
});
constructor.BindFunc("options_changed",
[](Rml::Variant& out) {
out = (cur_options != new_options);
});
options_handle = constructor.GetModelHandle();
}
std::unique_ptr<Rml::EventListenerInstancer> recomp::make_event_listener_instancer() { std::unique_ptr<Rml::EventListenerInstancer> recomp::make_event_listener_instancer() {
std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>(); std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>();
ret->register_event("start_game", ret->register_event("start_game",
[](Rml::Event& event) { [](Rml::Event& event) {
ultramodern::start_game(0); ultramodern::start_game(0);
set_current_menu(Menu::None); set_current_menu(Menu::Config);
} }
); );
ret->register_event("apply_options",
[](Rml::Event& event) {
cur_options = new_options;
options_handle.DirtyVariable("options_changed");
update_graphics_config(new_options);
});
return ret; return ret;
} }

View File

@ -565,7 +565,7 @@ public:
}; };
bool can_focus(Rml::Element* element) { bool can_focus(Rml::Element* element) {
return element->GetProperty(Rml::PropertyId::TabIndex)->Get<Rml::Style::TabIndex>() != Rml::Property(Rml::Style::TabIndex::None); return element->GetOwnerDocument() != nullptr && element->GetProperty(Rml::PropertyId::TabIndex)->Get<Rml::Style::TabIndex>() != Rml::Property(Rml::Style::TabIndex::None);
} }
Rml::Element* get_target(Rml::ElementDocument* document, Rml::Element* element) { Rml::Element* get_target(Rml::ElementDocument* document, Rml::Element* element) {
@ -660,8 +660,21 @@ struct {
// Revert focus to the previous element if focused on anything without a tab index. // Revert focus to the previous element if focused on anything without a tab index.
// This should prevent the user from losing focus on something that has no navigation. // This should prevent the user from losing focus on something that has no navigation.
if (focused && !can_focus(focused)) { if (focused && !can_focus(focused)) {
// If the previously focused element is still accepting focus, return focus to it.
if (prev_focused && can_focus(prev_focused)) {
prev_focused->Focus(); prev_focused->Focus();
} }
// Otherwise, check if the currently focused element has a "nav-return" attribute and focus that attribute's value if so.
else {
Rml::Variant* nav_return = focused->GetAttribute("nav-return");
if (nav_return && nav_return->GetType() == Rml::Variant::STRING) {
Rml::Element* return_element = current_document->GetElementById(nav_return->Get<std::string>());
if (return_element) {
return_element->Focus();
}
}
}
}
else { else {
prev_focused = current_document->GetFocusLeafNode(); prev_focused = current_document->GetFocusLeafNode();
} }
@ -693,6 +706,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
SDL_GetWindowSizeInPixels(window, &width, &height); SDL_GetWindowSizeInPixels(window, &width, &height);
UIContext.rml.context = Rml::CreateContext("main", Rml::Vector2i(width, height)); UIContext.rml.context = Rml::CreateContext("main", Rml::Vector2i(width, height));
recomp::make_ui_bindings(UIContext.rml.context);
Rml::Debugger::Initialise(UIContext.rml.context); Rml::Debugger::Initialise(UIContext.rml.context);
@ -730,7 +744,7 @@ bool recomp::try_deque_event(SDL_Event& out) {
return ui_event_queue.try_dequeue(out); return ui_event_queue.try_dequeue(out);
} }
std::atomic<recomp::Menu> open_menu = recomp::Menu::Config;//Launcher; std::atomic<recomp::Menu> open_menu = recomp::Menu::Launcher;
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) { void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) {
int num_keys; int num_keys;
@ -778,6 +792,9 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
int width, height; int width, height;
SDL_GetWindowSizeInPixels(window, &width, &height); SDL_GetWindowSizeInPixels(window, &width, &height);
// Scale the UI based on the window size with 720 vertical resolution as the reference point.
UIContext.rml.context->SetDensityIndependentPixelRatio(height / 720.0f);
UIContext.rml.render_interface->start(command_list, width, height); UIContext.rml.render_interface->start(command_list, width, height);
static int prev_width = 0; static int prev_width = 0;

33
ultramodern/config.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef __CONFIG_HPP__
#define __CONFIG_HPP__
#include "common/rt64_user_configuration.h"
namespace ultramodern {
enum class Resolution {
Original,
Original2x,
Auto,
OptionCount
};
enum class WindowMode {
Windowed,
Fullscreen,
OptionCount
};
struct GraphicsConfig {
Resolution res_option;
WindowMode wm_option;
RT64::UserConfiguration::AspectRatio ar_option;
RT64::UserConfiguration::Antialiasing msaa_option;
RT64::UserConfiguration::RefreshRate rr_option;
int rr_manual_value;
auto operator<=>(const GraphicsConfig& rhs) const = default;
};
void update_graphics_config(const GraphicsConfig& config);
};
#endif

View File

@ -13,6 +13,8 @@
#include "ultra64.h" #include "ultra64.h"
#include "ultramodern.hpp" #include "ultramodern.hpp"
#include "config.hpp"
#include "rt64_layer.h"
#include "recomp.h" #include "recomp.h"
#include "recomp_ui.h" #include "recomp_ui.h"
#include "rsp.h" #include "rsp.h"
@ -25,7 +27,11 @@ struct SwapBuffersAction {
uint32_t origin; uint32_t origin;
}; };
using Action = std::variant<SpTaskAction, SwapBuffersAction>; struct UpdateConfigAction {
ultramodern::GraphicsConfig config;
};
using Action = std::variant<SpTaskAction, SwapBuffersAction, UpdateConfigAction>;
static struct { static struct {
struct { struct {
@ -174,12 +180,6 @@ void dp_complete() {
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK); osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
} }
bool RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle);
void RT64SendDL(uint8_t* rdram, const OSTask* task);
void RT64UpdateScreen(uint32_t vi_origin);
void RT64ChangeWindow();
void RT64Shutdown();
uint8_t dmem[0x1000]; uint8_t dmem[0x1000];
uint16_t rspReciprocals[512]; uint16_t rspReciprocals[512];
uint16_t rspInverseSquareRoots[512]; uint16_t rspInverseSquareRoots[512];
@ -257,13 +257,21 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
} }
} }
void ultramodern::update_graphics_config(const ultramodern::GraphicsConfig& config) {
events_context.action_queue.enqueue(UpdateConfigAction{ config });
}
void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, ultramodern::WindowHandle window_handle) { void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, ultramodern::WindowHandle window_handle) {
using namespace std::chrono_literals; using namespace std::chrono_literals;
ultramodern::set_native_thread_name("Gfx Thread"); ultramodern::set_native_thread_name("Gfx Thread");
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal);
if (!RT64Init(rom, rdram, window_handle)) { ultramodern::GraphicsConfig cur_config{};
RT64::Application* application = RT64Init(rom, rdram, window_handle);
if (application == nullptr) {
throw std::runtime_error("Failed to initialize RT64!"); throw std::runtime_error("Failed to initialize RT64!");
} }
@ -286,10 +294,17 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
sp_complete(); sp_complete();
RT64SendDL(rdram, &task_action->task); RT64SendDL(rdram, &task_action->task);
dp_complete(); dp_complete();
} else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) { }
else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
events_context.vi.current_buffer = events_context.vi.next_buffer; events_context.vi.current_buffer = events_context.vi.next_buffer;
RT64UpdateScreen(swap_action->origin); RT64UpdateScreen(swap_action->origin);
} }
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
if (cur_config != config_action->config) {
RT64UpdateConfig(application, cur_config, config_action->config);
cur_config = config_action->config;
}
}
} }
} }
recomp::destroy_ui(); recomp::destroy_ui();

View File

@ -136,10 +136,10 @@ typedef struct {
PTR(u64) yield_data_ptr; PTR(u64) yield_data_ptr;
u32 yield_data_size; u32 yield_data_size;
} OSTask_t; } OSTask_s;
typedef union { typedef union {
OSTask_t t; OSTask_s t;
int64_t force_structure_alignment; int64_t force_structure_alignment;
} OSTask; } OSTask;