From 958808acb09986d2ff81492eb510cf7be26527f2 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 4 Feb 2024 22:55:15 -0500 Subject: [PATCH] Enable relevant RT64 enhancements, fix pause background corruption if song of soaring was played before ever pausing --- include/rt64_layer.h | 1 + patches/fixes.c | 80 +++++++++++++++++++++++++++++---------- patches/input.c | 2 +- patches/ui_patches.c | 13 +++---- src/game/config.cpp | 2 +- src/recomp/rt64_layer.cpp | 14 +++++++ ultramodern/events.cpp | 6 +++ 7 files changed, 90 insertions(+), 28 deletions(-) diff --git a/include/rt64_layer.h b/include/rt64_layer.h index a27c094..f7b0c41 100644 --- a/include/rt64_layer.h +++ b/include/rt64_layer.h @@ -14,6 +14,7 @@ namespace ultramodern { RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle); void RT64UpdateConfig(RT64::Application* application, const ultramodern::GraphicsConfig& old_config, const ultramodern::GraphicsConfig& new_config); +void RT64EnableInstantPresent(RT64::Application* application); void RT64SendDL(uint8_t* rdram, const OSTask* task); void RT64UpdateScreen(uint32_t vi_origin); void RT64ChangeWindow(); diff --git a/patches/fixes.c b/patches/fixes.c index da76518..f66ef1b 100644 --- a/patches/fixes.c +++ b/patches/fixes.c @@ -31,8 +31,6 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num s32 cur_y; u32 row; - gSegments[0x0D] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemLangSegment); - cur_y = PAGE_BG_HEIGHT / 2; // 2 verts per row plus 2 extra verts at the start and the end. @@ -74,10 +72,10 @@ s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 num // These are overlay symbols, so their addresses need to be offset to get their actual loaded vram address. // TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions. - s16** sVtxPageQuadsXRelocated = (s16**)((u8*)&sVtxPageQuadsX[0] + gKaleidoMgrOverlayTable[0].offset); - s16** sVtxPageQuadsWidthRelocated = (s16**)((u8*)&sVtxPageQuadsWidth[0] + gKaleidoMgrOverlayTable[0].offset); - s16** sVtxPageQuadsYRelocated = (s16**)((u8*)&sVtxPageQuadsY[0] + gKaleidoMgrOverlayTable[0].offset); - s16** sVtxPageQuadsHeightRelocated = (s16**)((u8*)&sVtxPageQuadsHeight[0] + gKaleidoMgrOverlayTable[0].offset); + s16** sVtxPageQuadsXRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsX); + s16** sVtxPageQuadsWidthRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsWidth); + s16** sVtxPageQuadsYRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsY); + s16** sVtxPageQuadsHeightRelocated = (s16**)KaleidoManager_GetRamAddr(sVtxPageQuadsHeight); s16 k = 60; @@ -128,8 +126,7 @@ typedef u8 bg_image_t[(2 + PAGE_BG_WIDTH) * (2 + PAGE_BG_HEIGHT)]; #define BG_IMAGE_COUNT 4 TexturePtr* bg_pointers[BG_IMAGE_COUNT]; -bg_image_t bg_images[BG_IMAGE_COUNT]; -u32 bg_image_count = 0; +bg_image_t bg_images[BG_IMAGE_COUNT] __attribute__((aligned(8))); void assemble_image(TexturePtr* textures, bg_image_t* image_out) { u8* pixels_out_start = *image_out; @@ -165,6 +162,58 @@ void assemble_image(TexturePtr* textures, bg_image_t* image_out) { } } +static bool assembled_kaleido_images = false; + +extern TexturePtr sMaskPageBgTextures[]; +extern TexturePtr sItemPageBgTextures[]; +extern TexturePtr sMapPageBgTextures[]; +extern TexturePtr sQuestPageBgTextures[]; + +extern void (*sKaleidoScopeUpdateFunc)(PlayState* play); +extern void (*sKaleidoScopeDrawFunc)(PlayState* play); + +extern void KaleidoScope_Update(PlayState* play); +extern void KaleidoScope_Draw(PlayState* play); + +void KaleidoUpdateWrapper(PlayState* play) { + KaleidoScope_Update(play); +} + +void KaleidoDrawWrapper(PlayState* play) { + // @recomp Update the background image pointers to reflect the overlay's load address. + bg_pointers[0] = KaleidoManager_GetRamAddr(sMaskPageBgTextures); + bg_pointers[1] = KaleidoManager_GetRamAddr(sItemPageBgTextures); + bg_pointers[2] = KaleidoManager_GetRamAddr(sMapPageBgTextures); + bg_pointers[3] = KaleidoManager_GetRamAddr(sQuestPageBgTextures); + + KaleidoScope_Draw(play); + + // @recomp Check if this is the first time kaleido has been drawn. If so, assemble the background textures + // into the full seamless image. + if (!assembled_kaleido_images) { + assembled_kaleido_images = true; + // Record the old value for segments 0x08 and 0x0D, then update them with the correct values so that segmented addresses + // can be converted in assemble_image. + uintptr_t old_segment_08 = gSegments[0x08]; + uintptr_t old_segment_0D = gSegments[0x0D]; + gSegments[0x08] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemSegment); + gSegments[0x0D] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemLangSegment); + assemble_image(KaleidoManager_GetRamAddr(sMaskPageBgTextures), &bg_images[0]); + assemble_image(KaleidoManager_GetRamAddr(sItemPageBgTextures), &bg_images[1]); + assemble_image(KaleidoManager_GetRamAddr(sMapPageBgTextures), &bg_images[2]); + assemble_image(KaleidoManager_GetRamAddr(sQuestPageBgTextures), &bg_images[3]); + gSegments[0x08] = old_segment_08; + gSegments[0x0D] = old_segment_0D; + } +} + +void KaleidoScopeCall_Init(PlayState* play) { + // @recomp Set the update and draw func pointers to the wrappers instead of the actual functions. + sKaleidoScopeUpdateFunc = KaleidoUpdateWrapper; + sKaleidoScopeDrawFunc = KaleidoDrawWrapper; + KaleidoSetup_Init(play); +} + // @recomp patched to fix bilerp seams. Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) { s32 i; @@ -172,24 +221,17 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures bg_image_t* cur_image = NULL; - // Check if this texture set has already been assembled into an image. + // Check if this texture set has been assembled into a full image. u32 image_index; - for (image_index = 0; image_index < bg_image_count; image_index++) { + for (image_index = 0; image_index < BG_IMAGE_COUNT; image_index++) { if (bg_pointers[image_index] == textures) { cur_image = &bg_images[image_index]; + break; } } - // If no image was found and there's a free image slot, assemble the image. - if (cur_image == NULL && image_index < BG_IMAGE_COUNT) { - assemble_image(textures, &bg_images[image_index]); - bg_pointers[image_index] = textures; - cur_image = &bg_images[image_index]; - bg_image_count++; - } - if (cur_image == NULL) { - // No image was found and there are no free slots. + // No image was found. return gfx; } diff --git a/patches/input.c b/patches/input.c index 2d31881..e835b68 100644 --- a/patches/input.c +++ b/patches/input.c @@ -24,7 +24,7 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) { static float filtered_gyro_x, filtered_gyro_y; static int applied_gyro_x, applied_gyro_y; - const float filter_factor = 0.50f; + const float filter_factor = 0.00f; // TODO remappable gyro reset button if (play->state.input[0].press.button & BTN_L) { diff --git a/patches/ui_patches.c b/patches/ui_patches.c index 8ee87dc..ad7b49d 100644 --- a/patches/ui_patches.c +++ b/patches/ui_patches.c @@ -394,17 +394,16 @@ void Interface_Draw(PlayState* play) { gEXSetScissorAlign(OVERLAY_DISP++, G_EX_ORIGIN_LEFT, G_EX_ORIGIN_RIGHT, 0, -margin_reduction, -SCREEN_WIDTH, margin_reduction, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); // @recomp Move the item being equipped from the center of the screen to the right edge as the timer counts down - if (gKaleidoMgrOverlayTable[0].loadedRamAddr != NULL) { - // These are overlay symbols, so their addresses need to be offset to get their actual loaded vram address. - // TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions. + if ((pauseCtx->state == PAUSE_STATE_MAIN) && ((pauseCtx->mainState == PAUSE_MAIN_STATE_EQUIP_ITEM) || + (pauseCtx->mainState == PAUSE_MAIN_STATE_EQUIP_MASK))) { extern s16 sEquipAnimTimer; extern s16 sMaskEquipAnimTimer; extern s16 sEquipState; extern s16 sMaskEquipState; - s16 equip_timer = *(s16*)((u8*)&sEquipAnimTimer + gKaleidoMgrOverlayTable[0].offset); - s16 mask_equip_timer = *(s16*)((u8*)&sMaskEquipAnimTimer + gKaleidoMgrOverlayTable[0].offset); - s16 equip_state = *(s16*)((u8*)&sEquipState + gKaleidoMgrOverlayTable[0].offset); - s16 mask_equip_state = *(s16*)((u8*)&sMaskEquipState + gKaleidoMgrOverlayTable[0].offset); + s16 equip_timer = *(s16*)KaleidoManager_GetRamAddr(&sEquipAnimTimer); + s16 mask_equip_timer = *(s16*)KaleidoManager_GetRamAddr(&sMaskEquipAnimTimer); + s16 equip_state = *(s16*)KaleidoManager_GetRamAddr(&sEquipState); + s16 mask_equip_state = *(s16*)KaleidoManager_GetRamAddr(&sMaskEquipState); s16 timer = MIN(equip_timer, mask_equip_timer); s32 max_timer = 10; diff --git a/src/game/config.cpp b/src/game/config.cpp index 1c0ff23..f67575d 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -114,7 +114,7 @@ void recomp::reset_graphics_options() { new_config.wm_option = ultramodern::WindowMode::Windowed; new_config.ar_option = RT64::UserConfiguration::AspectRatio::Expand; new_config.msaa_option = RT64::UserConfiguration::Antialiasing::MSAA4X; - new_config.rr_option = RT64::UserConfiguration::RefreshRate::Original; + new_config.rr_option = RT64::UserConfiguration::RefreshRate::Display; new_config.rr_manual_value = 60; ultramodern::set_graphics_config(new_config); } diff --git a/src/recomp/rt64_layer.cpp b/src/recomp/rt64_layer.cpp index da8eaa7..8280fd7 100644 --- a/src/recomp/rt64_layer.cpp +++ b/src/recomp/rt64_layer.cpp @@ -201,6 +201,13 @@ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHan device_max_msaa = compute_max_supported_aa(common_sample_counts); + // Force gbi depth branches to prevent LODs from kicking in. + ret->enhancementConfig.f3dex.forceBranch = true; + // Scale LODs based on the output resolution. + ret->enhancementConfig.textureLOD.scale = true; + + ret->updateEnhancementConfig(); + return ret; } @@ -260,6 +267,13 @@ void RT64UpdateConfig(RT64::Application* application, const ultramodern::Graphic } } +void RT64EnableInstantPresent(RT64::Application* application) { + // Enable the present early presentation mode for minimal latency. + application->enhancementConfig.presentation.mode = RT64::EnhancementConfiguration::Presentation::Mode::PresentEarly; + + application->updateEnhancementConfig(); +} + RT64::UserConfiguration::Antialiasing RT64MaxMSAA() { return device_max_msaa; } diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp index 4b381f6..cc025ff 100644 --- a/ultramodern/events.cpp +++ b/ultramodern/events.cpp @@ -268,6 +268,7 @@ ultramodern::GraphicsConfig ultramodern::get_graphics_config() { } 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; ultramodern::set_native_thread_name("Gfx Thread"); @@ -293,6 +294,11 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) { // Determine the action type and act on it if (const auto* task_action = std::get_if(&action)) { + // Turn on instant present if the game has been started and it hasn't been turned on yet. + if (ultramodern::is_game_started() && !enabled_instant_present) { + RT64EnableInstantPresent(application); + enabled_instant_present = true; + } // Tell the game that the RSP completed instantly. This will allow it to queue other task types, but it won't // start another graphics task until the RDP is also complete. Games usually preserve the RSP inputs until the RDP // is finished as well, so sending this early shouldn't be an issue in most cases.