From 1d151f28647257fb7f8cda6bf575e8efc329547d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 19 Aug 2023 00:48:09 +0200 Subject: [PATCH] port: implement some framebuffer effects cloaking and menu blur in a likely extremely slow way --- port/fast3d/gfx_api.h | 3 ++ port/fast3d/gfx_opengl.cpp | 69 +++++++++++++++++++++++++++++---- port/fast3d/gfx_pc.cpp | 40 ++++++++++++++++++- port/fast3d/gfx_rendering_api.h | 3 +- port/include/video.h | 5 +++ port/src/pdsched.c | 7 ++-- port/src/video.c | 20 ++++++++++ src/game/chr.c | 29 +++++++++++++- src/game/chrmgr.c | 9 +++++ src/game/menu.c | 14 +++++++ src/game/menugfx.c | 26 +++++++++++-- src/include/bss.h | 4 ++ src/include/gbiex.h | 36 +++++++++++++++++ 13 files changed, 245 insertions(+), 20 deletions(-) diff --git a/port/fast3d/gfx_api.h b/port/fast3d/gfx_api.h index 9c2151a11..27109a5fc 100644 --- a/port/fast3d/gfx_api.h +++ b/port/fast3d/gfx_api.h @@ -40,6 +40,9 @@ void gfx_set_target_fps(int); void gfx_set_maximum_frame_latency(int latency); void gfx_texture_cache_clear(void); int gfx_create_framebuffer(uint32_t width, uint32_t height); +void gfx_set_framebuffer(int fb, float noise_scale) ; +void gfx_reset_framebuffer(void); +void gfx_copy_framebuffer(int fb_dst, int fb_src, int left, int top); void gfx_get_pixel_depth_prepare(float x, float y); uint16_t gfx_get_pixel_depth(float x, float y); diff --git a/port/fast3d/gfx_opengl.cpp b/port/fast3d/gfx_opengl.cpp index 0cf0cc652..be7634553 100644 --- a/port/fast3d/gfx_opengl.cpp +++ b/port/fast3d/gfx_opengl.cpp @@ -808,8 +808,10 @@ typedef void (APIENTRY *DEBUGPROC)(GLenum source, const void *userParam); static void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *msg, const void *p) { - if (severity > 0x826B) + if (severity > 0x826B) { printf("GL: from %04x: type %04x: %s\n", source, type, msg); + fflush(stdout); + } } static void gfx_opengl_init(void) { @@ -944,14 +946,18 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width, fb.invert_y = opengl_invert_y; } -void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) { - Framebuffer& fb = framebuffers[fb_id]; - - if (noise_scale != 0.0f) { - current_noise_scale = 1.0f / noise_scale; +bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) { + if (fb_id < (int)framebuffers.size()) { + Framebuffer& fb = framebuffers[fb_id]; + if (noise_scale != 0.0f) { + current_noise_scale = 1.0f / noise_scale; + } + glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); + current_framebuffer = fb_id; + return true; + } else { + return false; } - glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); - current_framebuffer = fb_id; } void gfx_opengl_clear_framebuffer() { @@ -983,6 +989,52 @@ void gfx_opengl_select_texture_fb(int fb_id) { glBindTexture(GL_TEXTURE_2D, framebuffers[fb_id].clrbuf); } +void gfx_opengl_copy_framebuffer(int fb_dst, int fb_src, int left, int top) { + if (fb_dst >= (int)framebuffers.size() || fb_src >= (int)framebuffers.size()) { + return; + } + + const Framebuffer& src = framebuffers[fb_src]; + const Framebuffer& dst = framebuffers[fb_dst]; + + int srcX0, srcY0, srcX1, srcY1; + int dstX0, dstY0, dstX1, dstY1; + + dstX0 = dstY0 = 0; + dstX1 = dst.width; + dstY1 = dst.height; + + if (left >= 0 && top >= 0) { + // unscaled rect copy + srcX0 = left; + srcY0 = top; + srcX1 = left + dst.width; + srcY1 = top + dst.height; + } else { + // scaled full copy + srcX0 = 0; + srcY0 = 0; + srcX1 = src.width; + srcY1 = src.height; + } + + if (fb_src == 0) { + // flip the dst rect to mirror the image vertically + std::swap(dstY0, dstY1); + } + + glDisable(GL_SCISSOR_TEST); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, src.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst.fbo); + + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[current_framebuffer].fbo); + + glEnable(GL_SCISSOR_TEST); +} + void gfx_opengl_set_texture_filter(FilteringMode mode) { current_filter_mode = mode; gfx_texture_cache_clear(); @@ -1020,6 +1072,7 @@ struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_create_framebuffer, gfx_opengl_update_framebuffer_parameters, gfx_opengl_start_draw_to_framebuffer, + gfx_opengl_copy_framebuffer, gfx_opengl_clear_framebuffer, gfx_opengl_resolve_msaa_color_buffer, gfx_opengl_get_framebuffer_texture_id, diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index f4623b33e..c355b2a6d 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -2397,6 +2397,12 @@ static void gfx_run_dl(Gfx* cmd) { gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), imgData, texFlags, rawTexMetdata, (void*)i); break; } + case G_SETTIMG_FB_EXT: + gfx_flush(); + gfx_rapi->select_texture_fb(cmd->words.w1); + rdp.textures_changed[0] = false; + rdp.textures_changed[1] = false; + break; case G_LOADBLOCK: gfx_dp_load_block(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); break; @@ -2490,9 +2496,30 @@ static void gfx_run_dl(Gfx* cmd) { case G_SETCIMG: gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1)); break; + case G_SETFB_EXT: + gfx_flush(); + if (cmd->words.w1) { + // don't care about noise here + gfx_set_framebuffer(cmd->words.w1, 1.f); + fbActive = true; + } else { + gfx_reset_framebuffer(); + fbActive = false; + } + break; + case G_COPYFB_EXT: + gfx_copy_framebuffer(C0(12, 12), C0(0, 12), C1(16, 16), C1(0, 16)); + break; case G_RDPSETOTHERMODE: gfx_dp_set_other_mode(C0(0, 24), cmd->words.w1); break; + case G_INVALTEXCACHE_EXT: + if (cmd->words.w1) { + gfx_texture_cache_delete((const uint8_t *)seg_addr(cmd->words.w1)); + } else { + gfx_texture_cache_clear(); + } + break; case (uint8_t)G_RDPHALF_1: case (uint8_t)G_RDPHALF_2: case (uint8_t)G_RDPHALF_CONT: @@ -2700,6 +2727,17 @@ extern "C" void gfx_set_framebuffer(int fb, float noise_scale) { gfx_rapi->clear_framebuffer(); } -extern "C" void gfx_reset_framebuffer() { +extern "C" void gfx_copy_framebuffer(int fb_dst, int fb_src, int left, int top) { + if (fb_src == 0 && left > 0 && top > 0) { + // upscale the position + left = left * gfx_current_dimensions.width / gfx_current_native_viewport.width; + top = top * gfx_current_dimensions.height / gfx_current_native_viewport.height; + // flip Y + top = gfx_current_dimensions.height - top - 1; + } + gfx_rapi->copy_framebuffer(fb_dst, fb_src, left, top); +} + +extern "C" void gfx_reset_framebuffer(void) { gfx_rapi->start_draw_to_framebuffer(0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT); } diff --git a/port/fast3d/gfx_rendering_api.h b/port/fast3d/gfx_rendering_api.h index 0ac07629c..0831d3e1e 100644 --- a/port/fast3d/gfx_rendering_api.h +++ b/port/fast3d/gfx_rendering_api.h @@ -43,7 +43,8 @@ struct GfxRenderingAPI { void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth); - void (*start_draw_to_framebuffer)(int fb_id, float noise_scale); + bool (*start_draw_to_framebuffer)(int fb_id, float noise_scale); + void (*copy_framebuffer)(int fb_dst, int fb_src, int left, int top); void (*clear_framebuffer)(void); void (*resolve_msaa_color_buffer)(int fb_id_target, int fb_id_source); void* (*get_framebuffer_texture_id)(int fb_id); diff --git a/port/include/video.h b/port/include/video.h index c15cdbf69..9031cc49d 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -21,6 +21,11 @@ s32 videoGetHeight(void); f32 videoGetAspect(void); f32 videoGetPlayerFovY(void); +s32 videoCreateFramebuffer(u32 w, u32 h); +void videoSetFramebuffer(s32 target); +void videoResetFramebuffer(void); +void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top); + void videoResetTextureCache(void); #endif diff --git a/port/src/pdsched.c b/port/src/pdsched.c index b84bdda2a..4b15a2574 100644 --- a/port/src/pdsched.c +++ b/port/src/pdsched.c @@ -394,10 +394,9 @@ void schedUpdatePendingArtifacts(void) void schedConsiderScreenshot(void) { if (g_MenuData.screenshottimer == 1) { - // TODO - // if (IS8MB()) { - // menugfxCreateBlur(); - // } + if (IS8MB()) { + menugfxCreateBlur(); + } g_MenuData.screenshottimer = 0; } diff --git a/port/src/video.c b/port/src/video.c index eed3cb355..87a5e4aa7 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -134,6 +134,26 @@ f32 videoGetPlayerFovY(void) return playerFovY; } +s32 videoCreateFramebuffer(u32 w, u32 h) +{ + return gfx_create_framebuffer(w, h); +} + +void videoSetFramebuffer(s32 target) +{ + return gfx_set_framebuffer(target, 1.f); +} + +void videoResetFramebuffer(void) +{ + return gfx_reset_framebuffer(); +} + +void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top) +{ + gfx_copy_framebuffer(dst, src, left, top); +} + void videoResetTextureCache(void) { gfx_texture_cache_clear(); diff --git a/src/game/chr.c b/src/game/chr.c index 155bcfaa5..1f458bfda 100644 --- a/src/game/chr.c +++ b/src/game/chr.c @@ -47,10 +47,18 @@ #include "data.h" #include "gbiex.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "video.h" +#endif void rng2SetSeed(u32 seed); +#ifdef PLATFORM_N64 void *var8009ccc0[20]; +#else +s32 var8009ccc0[20]; +#endif + s32 g_NumChrs; s16 *g_Chrnums; s16 *g_ChrIndexes; @@ -6234,13 +6242,21 @@ Gfx *shieldhitRender(Gfx *gdl, struct prop *prop1, struct prop *prop2, s32 alpha gDPPipeSync(gdl++); gDPSetTextureLUT(gdl++, G_TT_NONE); +#ifndef PLATFORM_N64 gDPLoadTextureBlock(gdl++, var8009ccc0[index], G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, 0, G_TX_MIRROR | G_TX_WRAP, G_TX_MIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD); +#endif gDPSetCycleType(gdl++, G_CYC_1CYCLE); gDPSetRenderMode(gdl++, G_RM_AA_ZB_XLU_SURF, G_RM_AA_ZB_XLU_SURF2); gDPSetCombineMode(gdl++, G_CC_MODULATEI, G_CC_MODULATEI); gSPTexture(gdl++, 0xffff, 0xffff, 0, G_TX_RENDERTILE, G_ON); gDPSetTextureFilter(gdl++, G_TF_BILERP); gDPSetColorDither(gdl++, G_CD_BAYER); +#ifndef PLATFORM_N64 + gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, (((16 * G_IM_SIZ_16b_BYTES)+7)>>3), 0, 0, 0, + G_TX_MIRROR | G_TX_WRAP, 4, G_TX_NOLOD, G_TX_MIRROR | G_TX_WRAP, 4, G_TX_NOLOD); + gDPSetTileSize(gdl++, G_TX_RENDERTILE, 0, 0, 16 << G_TEXTURE_IMAGE_FRAC, 16 << G_TEXTURE_IMAGE_FRAC); + gDPSetFramebufferTextureEXT(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, var8009ccc0[index]); +#endif gdl = chrRenderShieldComponent(gdl, NULL, prop1, model, node, -7, -1, -1, 255); } else { @@ -6396,16 +6412,25 @@ Gfx *chrRenderCloak(Gfx *gdl, struct prop *chrprop, struct prop *thisprop) lrs = uls + 15; lrt = ult + 15; +#ifdef PLATFORM_N64 gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, OS_K0_TO_PHYSICAL(var8009ccc0[index])); gDPTileSync(gdl++); - gDPLoadTextureTile(gdl++, viGetBackBuffer(), G_IM_FMT_RGBA, G_IM_SIZ_16b, viGetWidth(), 0, uls, ult, lrs, lrt, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD); - gDPPipeSync(gdl++); gSPTextureRectangle(gdl++, 0, 0, 60, 60, G_TX_RENDERTILE, 0, 0, 4096, 1024); +#else + gDPCopyFramebufferEXT(gdl++, var8009ccc0[index], 0, uls, ult); + gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, + ((((lrs - uls + 1) * G_IM_SIZ_16b_BYTES)+7)>>3), 0, 0, 0, + G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOLOD); + gDPSetTileSize(gdl++, G_TX_RENDERTILE, + uls << G_TEXTURE_IMAGE_FRAC, ult << G_TEXTURE_IMAGE_FRAC, + lrs << G_TEXTURE_IMAGE_FRAC, lrt << G_TEXTURE_IMAGE_FRAC); +#endif } } } diff --git a/src/game/chrmgr.c b/src/game/chrmgr.c index 08dec6d76..94ec7fb96 100644 --- a/src/game/chrmgr.c +++ b/src/game/chrmgr.c @@ -6,6 +6,9 @@ #include "lib/memp.h" #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "video.h" +#endif void chrmgrReset(void) { @@ -34,7 +37,13 @@ void chrmgrReset(void) var80062960 = mempAlloc(ALIGN16(15 * sizeof(struct var80062960)), MEMPOOL_STAGE); for (i = 0; i < ARRAYCOUNT(var8009ccc0); i++) { +#ifdef PLATFORM_N64 var8009ccc0[i] = (void *)ALIGN64(mempAlloc(16 * 16 * sizeof(u16) + 0x40, MEMPOOL_STAGE)); +#else + if (!var8009ccc0[i]) { + var8009ccc0[i] = videoCreateFramebuffer(16, 16); + } +#endif } resetSomeStageThings(); diff --git a/src/game/menu.c b/src/game/menu.c index 4877f96ed..466d12bfb 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -5059,9 +5059,23 @@ Gfx *menuRenderBackgroundLayer1(Gfx *gdl, u8 bg, f32 frac) // Render the blurred background texture with full alpha gdl = menugfxRenderBgBlur(gdl, 0xffffff00 | alpha, 0, 0); +#ifdef PLATFORM_N64 // Render it twice more with half alpha and offset gdl = menugfxRenderBgBlur(gdl, 0xffffff00 | alpha >> 1, -30, -30); gdl = menugfxRenderBgBlur(gdl, 0xffffff00 | alpha >> 1, 30, 30); +#else + // HACK: we don't actually blur anything on the CPU, so + // render it offset to all cardinal and ordinal directions to achieve crappy box blur + const s32 card[2] = { -30, 30 }; + const s32 ord[2] = { -21, 21 }; // 30 / sqrt(2) + const u32 rgba = 0xffffff00 | alpha >> 3; + for (s32 i = 0; i < 2; ++i) { + gdl = menugfxRenderBgBlur(gdl, rgba, 0, card[i]); + gdl = menugfxRenderBgBlur(gdl, rgba, card[i], 0); + gdl = menugfxRenderBgBlur(gdl, rgba, ord[0], ord[i]); + gdl = menugfxRenderBgBlur(gdl, rgba, ord[i], ord[0]); + } +#endif } break; case MENUBG_BLACK: diff --git a/src/game/menugfx.c b/src/game/menugfx.c index b07061432..e7f84e332 100644 --- a/src/game/menugfx.c +++ b/src/game/menugfx.c @@ -17,6 +17,9 @@ #include "lib/mtx.h" #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "video.h" +#endif #define NUM_SUCCESS_PARTICLES 280 @@ -26,6 +29,10 @@ #define SAMPLE_HEIGHT 8 #define PXTOBYTES(val) ((val) * 2) +#ifndef PLATFORM_N64 +s32 g_MenuBlurFb = 0; +#endif + /** * Blur the gameplay background for the pause menu. * @@ -44,6 +51,7 @@ */ void menugfxCreateBlur(void) { +#ifdef PLATFORM_N64 u8 *fb = (u8 *) viGetFrontBuffer(); s32 dstx; s32 dsty; @@ -113,20 +121,25 @@ void menugfxCreateBlur(void) } g_ScaleX = 1; +#else + if (g_MenuBlurFb == 0) { + g_MenuBlurFb = videoCreateFramebuffer(BLURIMG_WIDTH, BLURIMG_HEIGHT); + } + // copy full viewport and downscale to 40x30 + videoCopyFramebuffer(g_MenuBlurFb, 0, -1, -1); +#endif } Gfx *menugfxRenderBgBlur(Gfx *gdl, u32 colour, s16 arg2, s16 arg3) { Col *colours; Vtx *vertices; -#if VERSION >= VERSION_PAL_BETA +#if (VERSION >= VERSION_PAL_BETA) || !defined(PLATFORM_N64) s32 width; s32 height; #endif -#ifdef PLATFORM_N64 // TODO if (IS4MB()) -#endif { return menugfxRenderGradient(gdl, 0, 0, viGetWidth(), viGetHeight(), 0xff, 0xff, 0xff); } @@ -141,6 +154,11 @@ Gfx *menugfxRenderBgBlur(Gfx *gdl, u32 colour, s16 arg2, s16 arg3) G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); +#ifndef PLATFORM_N64 + // let gDPLoadTextureBlock above set the size, then overwrite the texture selection with our fb texture + gDPSetFramebufferTextureEXT(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, BLURIMG_WIDTH, g_MenuBlurFb); +#endif + gDPPipeSync(gdl++); gDPSetCycleType(gdl++, G_CYC_1CYCLE); gDPSetAlphaCompare(gdl++, G_AC_NONE); @@ -166,7 +184,7 @@ Gfx *menugfxRenderBgBlur(Gfx *gdl, u32 colour, s16 arg2, s16 arg3) *(u16 *)&vertices[3].x = arg2; *(u16 *)&vertices[3].y = arg3 + SCREEN_240 * 10u + 50; vertices[3].z = -10; -#elif PAL +#elif PAL || !defined(PLATFORM_N64) width = viGetWidth() * 10; height = viGetHeight() * 10; diff --git a/src/include/bss.h b/src/include/bss.h index 67eb234e9..c656ce925 100644 --- a/src/include/bss.h +++ b/src/include/bss.h @@ -86,7 +86,11 @@ extern Mtx *var8009cc80; extern Mtx *var8009cc84; extern Mtx *var8009cc88; extern u8 *var8009cca0; +#ifdef PLATFORM_N64 extern void *var8009ccc0[20]; +#else +extern int var8009ccc0[20]; +#endif extern s32 g_NumChrs; extern s16 *g_Chrnums; extern s16 *g_ChrIndexes; diff --git a/src/include/gbiex.h b/src/include/gbiex.h index 4c311964c..e3d4d7d9a 100644 --- a/src/include/gbiex.h +++ b/src/include/gbiex.h @@ -180,4 +180,40 @@ #define G_CC_CUSTOM_26 TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL0, 0, ENVIRONMENT, 0 #define G_CC_CUSTOM_27 PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT +#ifndef PLATFORM_N64 + +/* Extended RDP commands */ + +#define G_SETFB_EXT 0x21 +#define G_COPYFB_EXT 0x41 +#define G_SETTIMG_FB_EXT 0x23 +#define G_INVALTEXCACHE_EXT 0x34 + +/* Extended RDP command macros */ + +#define gDPSetFramebufferTargetEXT(pkt, f, s, w, i) \ + gSetImage(pkt, G_SETFB_EXT, f, s, w, i) + +#define gDPSetFramebufferTextureEXT(pkt, f, s, w, i) \ + gSetImage(pkt, G_SETTIMG_FB_EXT, f, s, w, i) + +#define gDPCopyFramebufferEXT(pkt, dst, src, uls, ult) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_COPYFB_EXT, 24, 8) \ + | _SHIFTL(dst, 12, 12) | _SHIFTL(src, 0, 12); \ + _g->words.w1 = _SHIFTL(uls, 16, 16) | _SHIFTL(ult, 0, 16); \ +} + +#define gDPInvalTexCacheEXT(pkt, addr) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_INVALTEXCACHE_EXT, 24, 8) \ + _g->words.w1 = (uintptr_t)(addr); \ +} + +#endif + #endif