Improved motion blur at higher framerates, reorganized some patch code
This commit is contained in:
parent
569d86d901
commit
f6e11ed723
|
@ -130,6 +130,7 @@ set (SOURCES
|
|||
${CMAKE_SOURCE_DIR}/src/game/scene_table.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/game/debug.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/game/recomp_api.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||
|
|
|
@ -20,6 +20,7 @@ void RT64UpdateScreen(uint32_t vi_origin);
|
|||
void RT64ChangeWindow();
|
||||
void RT64Shutdown();
|
||||
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
|
||||
uint32_t RT64GetDisplayFramerate(RT64::Application* application);
|
||||
|
||||
void set_rt64_hooks();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "patches.h"
|
||||
#include "input.h"
|
||||
#include "misc_funcs.h"
|
||||
|
||||
void Message_FindMessage(PlayState* play, u16 textId);
|
||||
extern SceneEntranceTableEntry sSceneEntranceTable[];
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#include "patches.h"
|
||||
#include "graphics.h"
|
||||
|
||||
extern TransitionOverlay gTransitionOverlayTable[];
|
||||
extern Gfx sTransWipe3DL[];
|
||||
|
||||
#define THIS ((TransitionWipe3*)thisx)
|
||||
// @recomp patched to scale the transition based on aspect ratio
|
||||
void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
|
||||
Gfx* gfx = *gfxP;
|
||||
Mtx* modelView = &THIS->modelView[THIS->frame];
|
||||
f32 scale = 14.8f;
|
||||
Gfx* texScroll;
|
||||
|
||||
// @recomp Modify the scale based on the aspect ratio to make sure the transition circle covers the whole screen
|
||||
float original_aspect_ratio = ((float)SCREEN_WIDTH) / ((float)SCREEN_HEIGHT);
|
||||
scale *= recomp_get_aspect_ratio(original_aspect_ratio) / original_aspect_ratio;
|
||||
|
||||
THIS->frame ^= 1;
|
||||
gDPPipeSync(gfx++);
|
||||
texScroll = Gfx_BranchTexScroll(&gfx, THIS->scrollX, THIS->scrollY, 16, 64);
|
||||
gSPSegment(gfx++, 0x09, texScroll);
|
||||
gSPSegment(gfx++, 0x08, THIS->curTexture);
|
||||
gDPSetColor(gfx++, G_SETPRIMCOLOR, THIS->color.rgba);
|
||||
gDPSetColor(gfx++, G_SETENVCOLOR, THIS->color.rgba);
|
||||
gSPMatrix(gfx++, &THIS->projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
|
||||
gSPPerspNormalize(gfx++, THIS->normal);
|
||||
gSPMatrix(gfx++, &THIS->lookAt, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
|
||||
|
||||
if (scale != 1.0f) {
|
||||
guScale(modelView, scale, scale, 1.0f);
|
||||
gSPMatrix(gfx++, modelView, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
}
|
||||
// sTransWipe3DL is an overlay symbol, so its addresses need to be offset to get the actual loaded vram address.
|
||||
// TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions.
|
||||
ptrdiff_t reloc_offset;
|
||||
TransitionOverlay* overlay_entry = &gTransitionOverlayTable[FBDEMO_WIPE3];
|
||||
reloc_offset = (uintptr_t)Lib_PhysicalToVirtual(overlay_entry->loadInfo.addr) - (uintptr_t)overlay_entry->vramStart;
|
||||
gSPDisplayList(gfx++, (Gfx*)((u8*)sTransWipe3DL + reloc_offset));
|
||||
gDPPipeSync(gfx++);
|
||||
*gfxP = gfx;
|
||||
}
|
||||
|
||||
#undef THIS
|
||||
|
||||
|
||||
typedef enum {
|
||||
/* 0 */ MOTION_BLUR_OFF,
|
||||
/* 1 */ MOTION_BLUR_SETUP,
|
||||
/* 2 */ MOTION_BLUR_PROCESS
|
||||
} MotionBlurStatus;
|
||||
|
||||
extern u8 sMotionBlurStatus;
|
||||
extern s32 gFramerateDivisor;
|
||||
|
||||
// @recomp Motion blur works fine normally, but when running at a higher framerate the effect is much less pronounced
|
||||
// as the previous frames decay quicker due to there being more frames drawn in the same period of time.
|
||||
void Play_DrawMotionBlur(PlayState* this) {
|
||||
GraphicsContext* gfxCtx = this->state.gfxCtx;
|
||||
s32 alpha;
|
||||
Gfx* gfx;
|
||||
Gfx* gfxHead;
|
||||
|
||||
if (R_MOTION_BLUR_PRIORITY_ENABLED) {
|
||||
alpha = R_MOTION_BLUR_PRIORITY_ALPHA;
|
||||
|
||||
if (sMotionBlurStatus == MOTION_BLUR_OFF) {
|
||||
sMotionBlurStatus = MOTION_BLUR_SETUP;
|
||||
}
|
||||
} else if (R_MOTION_BLUR_ENABLED) {
|
||||
alpha = R_MOTION_BLUR_ALPHA;
|
||||
|
||||
if (sMotionBlurStatus == MOTION_BLUR_OFF) {
|
||||
sMotionBlurStatus = MOTION_BLUR_SETUP;
|
||||
}
|
||||
} else {
|
||||
alpha = 0;
|
||||
sMotionBlurStatus = MOTION_BLUR_OFF;
|
||||
}
|
||||
|
||||
if (sMotionBlurStatus != MOTION_BLUR_OFF) {
|
||||
OPEN_DISPS(gfxCtx);
|
||||
|
||||
gfxHead = POLY_OPA_DISP;
|
||||
gfx = Graph_GfxPlusOne(gfxHead);
|
||||
|
||||
gSPDisplayList(OVERLAY_DISP++, gfx);
|
||||
|
||||
this->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer;
|
||||
this->pauseBgPreRender.fbufSave = this->unk_18E64;
|
||||
|
||||
// @recomp Scale alpha based on the target framerate so that the blur effect decays at an equivalent rate
|
||||
// to how it does in the original game's framerate.
|
||||
alpha = (s32)(recomp_powf(alpha / 255.0f, 20.0f / recomp_get_target_framerate(gFramerateDivisor)) * 255.0f);
|
||||
|
||||
if (sMotionBlurStatus == MOTION_BLUR_PROCESS) {
|
||||
func_80170AE0(&this->pauseBgPreRender, &gfx, alpha);
|
||||
} else {
|
||||
sMotionBlurStatus = MOTION_BLUR_PROCESS;
|
||||
}
|
||||
|
||||
PreRender_SaveFramebuffer(&this->pauseBgPreRender, &gfx);
|
||||
|
||||
gSPEndDisplayList(gfx++);
|
||||
|
||||
Graph_BranchDlist(gfxHead, gfx);
|
||||
|
||||
POLY_OPA_DISP = gfx;
|
||||
|
||||
CLOSE_DISPS(gfxCtx);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "patch_helpers.h"
|
||||
|
||||
DECLARE_FUNC(float, recomp_get_aspect_ratio);
|
||||
DECLARE_FUNC(float, recomp_get_aspect_ratio, float);
|
||||
DECLARE_FUNC(s32, recomp_get_target_framerate, s32);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,11 +11,5 @@ typedef enum {
|
|||
extern RecompCameraMode recomp_camera_mode;
|
||||
|
||||
DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y);
|
||||
// TODO move these
|
||||
DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
|
||||
DECLARE_FUNC(void, recomp_exit);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
DECLARE_FUNC(u16, recomp_get_pending_warp);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __RECOMP_FUNCS_H__
|
||||
#define __RECOMP_FUNCS_H__
|
||||
|
||||
#include "patch_helpers.h"
|
||||
|
||||
DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
|
||||
DECLARE_FUNC(void, recomp_exit);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
DECLARE_FUNC(u16, recomp_get_pending_warp);
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
#include "patches.h"
|
||||
#include "input.h"
|
||||
#include "misc_funcs.h"
|
||||
#include "z64shrink_window.h"
|
||||
#include "overlays/gamestates/ovl_file_choose/z_file_select.h"
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
|
||||
int recomp_printf(const char* fmt, ...);
|
||||
float recomp_powf(float, float);
|
||||
|
||||
static inline void* actor_relocate(Actor* actor, void* addr) {
|
||||
return (void*)((uintptr_t)addr -
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "patches.h"
|
||||
#include "input.h"
|
||||
#include "misc_funcs.h"
|
||||
|
||||
void* proutPrintf(void* dst, const char* fmt, size_t size) {
|
||||
recomp_puts(fmt, size);
|
||||
|
|
|
@ -13,3 +13,5 @@ osSendMesg_recomp = 0x81000014;
|
|||
recomp_get_gyro_deltas = 0x81000018;
|
||||
recomp_get_aspect_ratio = 0x8100001C;
|
||||
recomp_get_pending_warp = 0x81000020;
|
||||
recomp_powf = 0x81000024;
|
||||
recomp_get_target_framerate = 0x81000028;
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
#include "patches.h"
|
||||
#include "graphics.h"
|
||||
|
||||
extern TransitionOverlay gTransitionOverlayTable[];
|
||||
extern Gfx sTransWipe3DL[];
|
||||
|
||||
#define THIS ((TransitionWipe3*)thisx)
|
||||
// @recomp patched to scale the transition based on aspect ratio
|
||||
void TransitionWipe3_Draw(void* thisx, Gfx** gfxP) {
|
||||
Gfx* gfx = *gfxP;
|
||||
Mtx* modelView = &THIS->modelView[THIS->frame];
|
||||
f32 scale = 14.8f;
|
||||
Gfx* texScroll;
|
||||
|
||||
// @recomp Modify the scale based on the aspect ratio to make sure the transition circle covers the whole screen
|
||||
scale *= (recomp_get_aspect_ratio() / (4.0f / 3.0f));
|
||||
|
||||
THIS->frame ^= 1;
|
||||
gDPPipeSync(gfx++);
|
||||
texScroll = Gfx_BranchTexScroll(&gfx, THIS->scrollX, THIS->scrollY, 16, 64);
|
||||
gSPSegment(gfx++, 0x09, texScroll);
|
||||
gSPSegment(gfx++, 0x08, THIS->curTexture);
|
||||
gDPSetColor(gfx++, G_SETPRIMCOLOR, THIS->color.rgba);
|
||||
gDPSetColor(gfx++, G_SETENVCOLOR, THIS->color.rgba);
|
||||
gSPMatrix(gfx++, &THIS->projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
|
||||
gSPPerspNormalize(gfx++, THIS->normal);
|
||||
gSPMatrix(gfx++, &THIS->lookAt, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
|
||||
|
||||
if (scale != 1.0f) {
|
||||
guScale(modelView, scale, scale, 1.0f);
|
||||
gSPMatrix(gfx++, modelView, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
}
|
||||
// sTransWipe3DL is an overlay symbol, so its addresses need to be offset to get the actual loaded vram address.
|
||||
// TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions.
|
||||
ptrdiff_t reloc_offset;
|
||||
TransitionOverlay* overlay_entry = &gTransitionOverlayTable[FBDEMO_WIPE3];
|
||||
reloc_offset = (uintptr_t)Lib_PhysicalToVirtual(overlay_entry->loadInfo.addr) - (uintptr_t)overlay_entry->vramStart;
|
||||
gSPDisplayList(gfx++, (Gfx*)((u8*)sTransWipe3DL + reloc_offset));
|
||||
gDPPipeSync(gfx++);
|
||||
*gfxP = gfx;
|
||||
}
|
||||
|
||||
#undef THIS
|
|
@ -3,8 +3,6 @@
|
|||
#include "recomp_helpers.h"
|
||||
#include "recomp_input.h"
|
||||
#include "../ultramodern/ultramodern.hpp"
|
||||
#include "../patches/input.h"
|
||||
#include "../patches/graphics.h"
|
||||
|
||||
// Arrays that hold the mappings for every input for keyboard and controller respectively.
|
||||
using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>;
|
||||
|
@ -103,36 +101,3 @@ void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
|||
*x_out = std::clamp(cur_x, -1.0f, 1.0f);
|
||||
*y_out = std::clamp(cur_y, -1.0f, 1.0f);
|
||||
}
|
||||
|
||||
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
recomp::poll_inputs();
|
||||
}
|
||||
|
||||
// TODO move these
|
||||
extern "C" void recomp_puts(uint8_t* rdram, recomp_context* ctx) {
|
||||
PTR(char) cur_str = _arg<0, PTR(char)>(rdram, ctx);
|
||||
u32 length = _arg<1, u32>(rdram, ctx);
|
||||
|
||||
for (u32 i = 0; i < length; i++) {
|
||||
fputc(MEM_B(i, (gpr)cur_str), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void recomp_exit(uint8_t* rdram, recomp_context* ctx) {
|
||||
ultramodern::quit();
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_gyro_deltas(uint8_t* rdram, recomp_context* ctx) {
|
||||
float* x_out = _arg<0, float*>(rdram, ctx);
|
||||
float* y_out = _arg<1, float*>(rdram, ctx);
|
||||
|
||||
recomp::get_gyro_deltas(x_out, y_out);
|
||||
}
|
||||
|
||||
#include "recomp_ui.h"
|
||||
extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
|
||||
int width, height;
|
||||
recomp::get_window_size(width, height);
|
||||
|
||||
_return(ctx, static_cast<float>(width) / height);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
#include <cmath>
|
||||
|
||||
#include "recomp.h"
|
||||
#include "recomp_input.h"
|
||||
#include "recomp_ui.h"
|
||||
#include "recomp_helpers.h"
|
||||
#include "../patches/input.h"
|
||||
#include "../patches/graphics.h"
|
||||
#include "../ultramodern/ultramodern.hpp"
|
||||
#include "../ultramodern/config.hpp"
|
||||
|
||||
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
recomp::poll_inputs();
|
||||
}
|
||||
|
||||
extern "C" void recomp_puts(uint8_t* rdram, recomp_context* ctx) {
|
||||
PTR(char) cur_str = _arg<0, PTR(char)>(rdram, ctx);
|
||||
u32 length = _arg<1, u32>(rdram, ctx);
|
||||
|
||||
for (u32 i = 0; i < length; i++) {
|
||||
fputc(MEM_B(i, (gpr)cur_str), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void recomp_exit(uint8_t* rdram, recomp_context* ctx) {
|
||||
ultramodern::quit();
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_gyro_deltas(uint8_t* rdram, recomp_context* ctx) {
|
||||
float* x_out = _arg<0, float*>(rdram, ctx);
|
||||
float* y_out = _arg<1, float*>(rdram, ctx);
|
||||
|
||||
recomp::get_gyro_deltas(x_out, y_out);
|
||||
}
|
||||
|
||||
extern "C" void recomp_powf(uint8_t* rdram, recomp_context* ctx) {
|
||||
float a = _arg<0, float>(rdram, ctx);
|
||||
float b = ctx->f14.fl; //_arg<1, float>(rdram, ctx);
|
||||
|
||||
_return(ctx, std::powf(a, b));
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_target_framerate(uint8_t* rdram, recomp_context* ctx) {
|
||||
int frame_divisor = _arg<0, u32>(rdram, ctx);
|
||||
|
||||
_return(ctx, ultramodern::get_target_framerate(60 / frame_divisor));
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
|
||||
ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config();
|
||||
float original = _arg<0, float>(rdram, ctx);
|
||||
int width, height;
|
||||
recomp::get_window_size(width, height);
|
||||
|
||||
switch (graphics_config.ar_option) {
|
||||
case RT64::UserConfiguration::AspectRatio::Original:
|
||||
default:
|
||||
_return(ctx, original);
|
||||
return;
|
||||
case RT64::UserConfiguration::AspectRatio::Expand:
|
||||
_return(ctx, static_cast<float>(width) / height);
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -265,3 +265,7 @@ void RT64EnableInstantPresent(RT64::Application* application) {
|
|||
RT64::UserConfiguration::Antialiasing RT64MaxMSAA() {
|
||||
return device_max_msaa;
|
||||
}
|
||||
|
||||
uint32_t RT64GetDisplayFramerate(RT64::Application* application) {
|
||||
return application->presentQueue->ext.sharedResources->swapChainRate;
|
||||
}
|
||||
|
|
|
@ -267,6 +267,23 @@ ultramodern::GraphicsConfig ultramodern::get_graphics_config() {
|
|||
return cur_config;
|
||||
}
|
||||
|
||||
std::atomic_uint32_t display_refresh_rate = 60;
|
||||
|
||||
uint32_t ultramodern::get_target_framerate(uint32_t original) {
|
||||
ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config();
|
||||
|
||||
switch (graphics_config.rr_option) {
|
||||
case RT64::UserConfiguration::RefreshRate::Original:
|
||||
case RT64::UserConfiguration::RefreshRate::OriginalDelay:
|
||||
default:
|
||||
return original;
|
||||
case RT64::UserConfiguration::RefreshRate::Manual:
|
||||
return graphics_config.rr_manual_value;
|
||||
case RT64::UserConfiguration::RefreshRate::Display:
|
||||
return display_refresh_rate.load();
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, ultramodern::WindowHandle window_handle) {
|
||||
bool enabled_instant_present = false;
|
||||
using namespace std::chrono_literals;
|
||||
|
@ -310,6 +327,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
|||
else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||
events_context.vi.current_buffer = events_context.vi.next_buffer;
|
||||
RT64UpdateScreen(swap_action->origin);
|
||||
display_refresh_rate = RT64GetDisplayFramerate(application);
|
||||
}
|
||||
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
|
||||
ultramodern::GraphicsConfig new_config = cur_config;
|
||||
|
|
|
@ -94,6 +94,8 @@ void send_si_message();
|
|||
uint32_t get_speed_multiplier();
|
||||
std::chrono::system_clock::time_point get_start();
|
||||
std::chrono::system_clock::duration time_since_start();
|
||||
void get_window_size(uint32_t& width, uint32_t& height);
|
||||
uint32_t get_target_framerate(uint32_t original);
|
||||
|
||||
// Audio
|
||||
void init_audio();
|
||||
|
|
Loading…
Reference in New Issue