diff --git a/port/fast3d/gfx_api.h b/port/fast3d/gfx_api.h index 4a40ef6bb..17a1a1310 100644 --- a/port/fast3d/gfx_api.h +++ b/port/fast3d/gfx_api.h @@ -41,10 +41,11 @@ void gfx_set_target_fps(int); void gfx_set_maximum_frame_latency(int latency); void gfx_texture_cache_clear(void); void gfx_texture_cache_delete(const uint8_t *orig_addr); -int gfx_create_framebuffer(uint32_t width, uint32_t height); +int gfx_create_framebuffer(uint32_t width, uint32_t height, int upscale, int autoresize); +void gfx_resize_framebuffer(int fb, uint32_t width, uint32_t height, int upscale, int autoresize); 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_copy_framebuffer(int fb_dst, int fb_src, int left, int top, int use_back); 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 e48ef002b..eb92631a7 100644 --- a/port/fast3d/gfx_opengl.cpp +++ b/port/fast3d/gfx_opengl.cpp @@ -921,7 +921,9 @@ static void gfx_opengl_init(void) { // maybe replace this with sysFatalError, though the GLSL compiler will cause that later anyway } - if (!gfx_opengl_supports_framebuffers()) { + if (!gfx_framebuffers_enabled) { + sysLogPrintf(LOG_WARNING, "GL: framebuffer effects disabled by user"); + } else if (!gfx_opengl_supports_framebuffers()) { sysLogPrintf(LOG_WARNING, "GL: GL_ARB_framebuffer_object unsupported, framebuffer effects disabled"); gfx_framebuffers_enabled = false; } @@ -1110,7 +1112,7 @@ 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) { +void gfx_opengl_copy_framebuffer(int fb_dst, int fb_src, int left, int top, bool flip_y, bool use_back) { if (!gfx_framebuffers_enabled || fb_dst >= (int)framebuffers.size() || fb_src >= (int)framebuffers.size()) { return; } @@ -1139,16 +1141,22 @@ void gfx_opengl_copy_framebuffer(int fb_dst, int fb_src, int left, int top) { 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); + if (flip_y) { + // flip the dst rect to mirror the image vertically + std::swap(dstY0, dstY1); + } + + if (fb_src == 0) { + glReadBuffer(use_back ? GL_BACK : GL_FRONT); + } else { + glReadBuffer(GL_COLOR_ATTACHMENT0); + } + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[current_framebuffer].fbo); diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index fdf46262f..75efc026a 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -242,6 +242,7 @@ static uintptr_t segmentPointers[16]; struct FBInfo { uint32_t orig_width, orig_height; uint32_t applied_width, applied_height; + bool upscale, autoresize; }; static bool fbActive = 0; @@ -2152,10 +2153,10 @@ static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int3 // lrx, lry, ulx, uly are U10.2 // lrs, lrt are S10.5 - int16_t width = !flip ? lrx - ulx : lry - uly; - int16_t height = !flip ? lry - uly : lrx - ulx; - float lrs = ((uls << 7) + dsdx * width) >> 7; - float lrt = ((ult << 7) + dtdy * height) >> 7; + const int16_t width = flip ? lry - uly : lrx - ulx; + const int16_t height = flip ? lrx - ulx : lry - uly; + const float lrs = ((uls << 7) + dsdx * width) >> 7; + const float lrt = ((ult << 7) + dtdy * height) >> 7; struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; @@ -2193,6 +2194,53 @@ static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int3 rdp.combine_mode = saved_combine_mode; } +static void gfx_dp_image_rectangle(int32_t tile, int32_t w, int32_t h, + int32_t ulx, int32_t uly, int16_t uls, int16_t ult, + int32_t lrx, int32_t lry, int16_t lrs, int16_t lrt) { + uint64_t saved_combine_mode = rdp.combine_mode; + + struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; + struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; + struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2]; + struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3]; + ul->u = uls * 32; + ul->v = ult * 32; + lr->u = lrs * 32; + lr->v = lrt * 32; + ll->u = uls * 32; + ll->v = lrt * 32; + ur->u = lrs * 32; + ur->v = ult * 32; + + // ensure we have the correct texture size + rdp.texture_tile[tile].line_size_bytes = w << rdp.texture_tile[tile].siz >> 1; + rdp.texture_tile[tile].width = w; + rdp.texture_tile[tile].height = h; + rdp.texture_tile[tile].cms = 0; + rdp.texture_tile[tile].cmt = 0; + rdp.texture_tile[tile].shifts = 0; + rdp.texture_tile[tile].shiftt = 0; + auto& loadtex = rdp.loaded_texture[rdp.texture_tile[tile].tmem]; + loadtex.full_image_line_size_bytes = loadtex.line_size_bytes = rdp.texture_tile[tile].line_size_bytes; + loadtex.size_bytes = loadtex.orig_size_bytes = loadtex.full_size_bytes = loadtex.line_size_bytes * h; + + uint8_t saved_tile = rdp.first_tile_index; + if (saved_tile != tile) { + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } + rdp.first_tile_index = tile; + + gfx_draw_rectangle(ulx, uly, lrx, lry); + if (saved_tile != tile) { + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } + rdp.first_tile_index = saved_tile; + + rdp.combine_mode = saved_combine_mode; +} + static void gfx_dp_fill_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { if (rdp.color_image_address == rdp.z_buf_address) { // Don't clear Z buffer here since we already did it with glClear @@ -2529,9 +2577,11 @@ static void gfx_run_dl(Gfx* cmd) { case G_TEXRECT_WIDE_EXT: { int32_t lrx, lry, tile, ulx, uly; uint32_t uls, ult, dsdx, dtdy; + bool flip; lrx = (int32_t)((C0(0, 24) << 8)) >> 8; lry = (int32_t)((C1(0, 24) << 8)) >> 8; tile = C1(24, 3); + flip = C1(27, 1); ++cmd; ulx = (int32_t)((C0(0, 24) << 8)) >> 8; uly = (int32_t)((C1(0, 24) << 8)) >> 8; @@ -2540,7 +2590,27 @@ static void gfx_run_dl(Gfx* cmd) { ult = C0(0, 16); dsdx = C1(16, 16); dtdy = C1(0, 16); - gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, opcode == G_TEXRECTFLIP); + gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, flip); + break; + } + case G_IMAGERECT_EXT: { + int16_t tile, iw, ih; + int16_t x0, y0, s0, t0; + int16_t x1, y1, s1, t1; + tile = C0(0, 3); + iw = C1(16, 16); + ih = C1(0, 16); + ++cmd; + x0 = C0(16, 16); + y0 = C0(0, 16); + s0 = C1(16, 16); + t0 = C1(0, 16); + ++cmd; + x1 = C0(16, 16); + y1 = C0(0, 16); + s1 = C1(16, 16); + t1 = C1(0, 16); + gfx_dp_image_rectangle(tile, iw, ih, x0, y0, s0, t0, x1, y1, s1, t1); break; } case G_SETSCISSOR: @@ -2564,7 +2634,7 @@ static void gfx_run_dl(Gfx* cmd) { } break; case G_COPYFB_EXT: - gfx_copy_framebuffer(C0(12, 12), C0(0, 12), C1(16, 16), C1(0, 16)); + gfx_copy_framebuffer(C0(11, 11), C0(0, 11), (int16_t)C1(16, 16), (int16_t)C1(0, 16), C0(22, 1)); break; case G_RDPSETOTHERMODE: gfx_dp_set_other_mode(C0(0, 24), cmd->words.w1); @@ -2618,6 +2688,11 @@ extern "C" void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAP game_framebuffer = gfx_rapi->create_framebuffer(); game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer(); + if (gfx_msaa_level > 1 && !gfx_framebuffers_enabled) { + sysLogPrintf(LOG_WARNING, "F3D: MSAA set to %d, but framebuffers are not available; disabling", gfx_msaa_level); + gfx_msaa_level = 1; + } + for (int i = 0; i < 16; i++) { segmentPointers[i] = 0; } @@ -2665,12 +2740,22 @@ extern "C" void gfx_start_frame(void) { if (gfx_current_dimensions.height != gfx_prev_dimensions.height) { for (auto& fb : framebuffers) { - uint32_t width = fb.second.orig_width, height = fb.second.orig_height; - gfx_adjust_width_height_for_scale(width, height); - if (width != fb.second.applied_width || height != fb.second.applied_height) { - gfx_rapi->update_framebuffer_parameters(fb.first, width, height, 1, true, true, true, true); - fb.second.applied_width = width; - fb.second.applied_height = height; + uint32_t width, height, msaa; + if (fb.second.autoresize) { + if (fb.second.upscale) { + width = fb.second.orig_width; + height = fb.second.orig_height; + gfx_adjust_width_height_for_scale(width, height); + } else { + // assume this is a fullscreen fb + width = gfx_current_dimensions.width; + height = gfx_current_dimensions.height; + } + if (width != fb.second.applied_width || height != fb.second.applied_height) { + gfx_rapi->update_framebuffer_parameters(fb.first, width, height, 1, true, true, true, true); + fb.second.applied_width = width; + fb.second.applied_height = height; + } } } } @@ -2768,29 +2853,58 @@ extern "C" void gfx_set_target_fps(int fps) { gfx_wapi->set_target_fps(fps); } -extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height) { - uint32_t orig_width = width, orig_height = height; - gfx_adjust_width_height_for_scale(width, height); +extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height, int upscale, int autoresize) { int fb = gfx_rapi->create_framebuffer(); - gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true); - framebuffers[fb] = { orig_width, orig_height, width, height }; + gfx_resize_framebuffer(fb, width, height, upscale, autoresize); return fb; } +extern "C" void gfx_resize_framebuffer(int fb, uint32_t width, uint32_t height, int upscale, int autoresize) { + uint32_t orig_width, orig_height; + + if (width && height) { + // user-specified size + orig_width = width; + orig_height = height; + if (upscale) { + gfx_adjust_width_height_for_scale(width, height); + } + gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true); + } else { + // same size as main fb + orig_width = width = gfx_current_dimensions.width; + orig_height = height = gfx_current_dimensions.height; + upscale = false; + autoresize = true; + gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true); + } + + framebuffers[fb] = { orig_width, orig_height, width, height, (bool)upscale, (bool)autoresize }; +} + extern "C" void gfx_set_framebuffer(int fb, float noise_scale) { gfx_rapi->start_draw_to_framebuffer(fb, noise_scale); gfx_rapi->clear_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; +extern "C" void gfx_copy_framebuffer(int fb_dst, int fb_src, int left, int top, int use_back) { + const bool is_main_fb = (fb_src == 0); + + if (is_main_fb) { + if (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; + } + if (use_back && gfx_msaa_level > 1) { + // read from the framebuffer we've been rendering to + fb_src = game_framebuffer; + } } - gfx_rapi->copy_framebuffer(fb_dst, fb_src, left, top); + + gfx_rapi->copy_framebuffer(fb_dst, fb_src, left, top, is_main_fb, (bool)use_back); } extern "C" void gfx_reset_framebuffer(void) { diff --git a/port/fast3d/gfx_rendering_api.h b/port/fast3d/gfx_rendering_api.h index 69c56fd7b..b31aa4ba7 100644 --- a/port/fast3d/gfx_rendering_api.h +++ b/port/fast3d/gfx_rendering_api.h @@ -44,7 +44,7 @@ struct GfxRenderingAPI { bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth); 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 (*copy_framebuffer)(int fb_dst, int fb_src, int left, int top, bool flip_y, bool use_back); 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 4f6f814c0..b067ecbc3 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -23,10 +23,12 @@ u32 videoGetTextureFilter2D(void); void videoSetWindowOffset(s32 x, s32 y); -s32 videoCreateFramebuffer(u32 w, u32 h); +s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); void videoResetFramebuffer(void); void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top); +void videoResizeFramebuffer(s32 target, u32 w, u32 h, s32 upscale, s32 autoresize); +s32 videoFramebuffersSupported(void); void videoResetTextureCache(void); void videoFreeCachedTexture(const void *texptr); diff --git a/port/src/pdsched.c b/port/src/pdsched.c index fa6dbc379..48ce5a26d 100644 --- a/port/src/pdsched.c +++ b/port/src/pdsched.c @@ -102,6 +102,9 @@ u32 var8005cea4 = 0; OSScMsg g_SchedRspMsg = {OS_SC_RSP_MSG}; bool g_SchedIsFirstTask = true; +s32 g_PrevFrameFb = -1; +s32 g_PrevFrameCapTimer = -1; + void schedSetCrashEnable1(bool enable) { g_SchedCrashEnable1 = enable; @@ -176,6 +179,8 @@ void osCreateScheduler(OSSched *sc, OSThread *thread, u8 mode, u32 numFields) osViSetEvent(&sc->interruptQ, (OSMesg)VIDEO_MSG, numFields); schedInitCrashLastRendered(); + + g_PrevFrameFb = videoCreateFramebuffer(0, 0, false, true); } void osScAddClient(OSSched *sc, OSScClient *c, OSMesgQueue *msgQ, bool is30fps) @@ -386,6 +391,13 @@ void schedConsiderScreenshot(void) g_MenuData.screenshottimer = 0; } + if (g_PrevFrameCapTimer == 0) { + videoCopyFramebuffer(g_PrevFrameFb, 0, -1, -1); + g_PrevFrameCapTimer = -1; + } else if (g_PrevFrameCapTimer > 0) { + --g_PrevFrameCapTimer; + } + if (g_MenuData.screenshottimer >= 2) { g_MenuData.screenshottimer--; } diff --git a/port/src/video.c b/port/src/video.c index ae1612f58..48fbd4821 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -33,6 +33,8 @@ s32 videoInit(void) const s32 h = configGetInt("Video.DefaultHeight", 480); const bool fs = configGetInt("Video.DefaultFullscreen", false); + gfx_framebuffers_enabled = (bool)configGetIntClamped("Video.FramebufferEffects", 1, 0, 1); + gfx_msaa_level = configGetInt("Video.MSAA", 0); if (gfx_msaa_level < 1 || gfx_msaa_level > 16) { gfx_msaa_level = 1; @@ -148,9 +150,9 @@ void videoSetWindowOffset(s32 x, s32 y) gfx_current_game_window_viewport.y = y; } -s32 videoCreateFramebuffer(u32 w, u32 h) +s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize) { - return gfx_create_framebuffer(w, h); + return gfx_create_framebuffer(w, h, upscale, autoresize); } void videoSetFramebuffer(s32 target) @@ -163,9 +165,20 @@ void videoResetFramebuffer(void) return gfx_reset_framebuffer(); } +s32 videoFramebuffersSupported(void) +{ + return gfx_framebuffers_enabled; +} + +void videoResizeFramebuffer(s32 target, u32 w, u32 h, s32 upscale, s32 autoresize) +{ + gfx_resize_framebuffer(target, w, h, upscale, autoresize); +} + void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top) { - gfx_copy_framebuffer(dst, src, left, top); + // assume immediate copies always read the front buffer + gfx_copy_framebuffer(dst, src, left, top, false); } void videoResetTextureCache(void) diff --git a/src/game/bondview.c b/src/game/bondview.c index 80c4dbeb4..b30dc3369 100644 --- a/src/game/bondview.c +++ b/src/game/bondview.c @@ -20,6 +20,9 @@ #include "data.h" #include "types.h" #include "gbiex.h" +#ifndef PLATFORM_N64 +#include "video.h" +#endif #ifdef AVOID_UB char var800a41c0[26]; @@ -281,6 +284,12 @@ Gfx *bviewDrawMotionBlur(Gfx *gdl, u32 colour, u32 alpha) var8007f844 = 0; +#ifndef PLATFORM_N64 + if (!videoFramebuffersSupported()) { + return gdl; + } +#endif + mainOverrideVariable("sfxxx", &sfxxx); fxxx = sfxxx / 1000.0f; mainOverrideVariable("sfyyy", &sfyyy); @@ -291,10 +300,19 @@ Gfx *bviewDrawMotionBlur(Gfx *gdl, u32 colour, u32 alpha) somefloat = (viewheight - viewheight / fyyy) * 0.5f; gdl = bviewPrepareStaticRgba16(gdl, colour, newalpha); +#ifdef PLATFORM_N64 for (i = viewtop; i < viewtop + viewheight; i++) { gdl = bviewCopyPixels(gdl, fb, viewtop + (s32)somefloat, 5, i, fxxx, viewleft, viewwidth); somefloat += 1.0f / fyyy; } +#else + gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_PrevFrameFb); + gSPImageRectangleEXT(gdl++, + viewleft << 2, viewtop << 2, viewleft, viewtop, + (viewleft + viewwidth) << 2, (viewtop + viewheight) << 2, viewleft + viewwidth, viewtop + viewheight, + 0, videoGetNativeWidth(), videoGetNativeHeight()); + g_PrevFrameCapTimer = 0; +#endif return gdl; } @@ -498,6 +516,12 @@ Gfx *bviewDrawZoomBlur(Gfx *gdl, u32 colour, s32 alpha, f32 arg3, f32 arg4) return gdl; } +#ifndef PLATFORM_N64 + if (!videoFramebuffersSupported()) { + return gdl; + } +#endif + strcpy(var800a41c0, "stretchBlurGfx"); gDPPipeSync(gdl++); @@ -506,10 +530,27 @@ Gfx *bviewDrawZoomBlur(Gfx *gdl, u32 colour, s32 alpha, f32 arg3, f32 arg4) gdl = bviewPrepareStaticRgba16(gdl, colour, alpha); +#ifdef PLATFORM_N64 for (i = viewtop; i < viewtop + viewheight; i++) { gdl = bviewCopyPixels(gdl, fb, (s32)somefloat + viewtop, 5, i, arg3, viewleft, viewwidth); somefloat += 1.0f / arg4; } +#else + const f32 xcenter = viewleft + viewwidth * 0.5f; + const f32 ycenter = viewtop + viewheight * 0.5f; + const f32 halfw = viewwidth * 0.5f * arg3; + const f32 halfh = viewheight * 0.5f * arg4; + const s32 left = xcenter - halfw; + const s32 top = ycenter - halfh; + const s32 right = xcenter + halfw; + const s32 bottom = ycenter + halfh; + gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_PrevFrameFb); + gSPImageRectangleEXT(gdl++, + left << 2, top << 2, viewleft, viewtop, + right << 2, bottom << 2, viewleft + viewwidth, viewtop + viewheight, + 0, videoGetNativeWidth(), videoGetNativeHeight()); + g_PrevFrameCapTimer = 0; +#endif return gdl; } @@ -534,6 +575,30 @@ f32 bview0f142d74(s32 arg0, f32 arg1, f32 arg2, f32 arg3) return result; } +#ifndef PLATFORM_N64 + +static inline Gfx *bviewDrawFisheyeLine(Gfx *gdl, s32 viewleft, s32 viewwidth, s32 y, f32 scale) +{ + if (!videoFramebuffersSupported()) { + return gdl; + } + + const f32 orighalfw = viewwidth * 0.5f; + const f32 xcenter = viewleft + orighalfw; + const f32 halfw = orighalfw * scale; + const s32 left = xcenter - halfw; + const s32 right = xcenter + halfw; + + gSPImageRectangleEXT(gdl++, + left << 2, y << 2, viewleft, y, + right << 2, (y + 1) << 2, viewleft + viewwidth, y + 1, + 0, videoGetNativeWidth(), videoGetNativeHeight()); + + return gdl; +} + +#endif + /** * Draw the fisheye curved effect when using an eyespy. * @@ -648,6 +713,12 @@ Gfx *bviewDrawFisheye(Gfx *gdl, u32 colour, u32 alpha, s32 shuttertime60, s8 sta gdl = bviewPrepareStaticRgba16(gdl, colour, alpha); +#ifndef PLATFORM_N64 + // make a copy of the current back buffer contents that we will be using as a texture + gDPCopyFramebufferEXT(gdl++, g_PrevFrameFb, 0, 0, 0, G_ON); + gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_PrevFrameFb); +#endif + if (starting) { for (i = viewtop; i < viewtop + viewheight; i++) { if (i % 2) { @@ -655,7 +726,11 @@ Gfx *bviewDrawFisheye(Gfx *gdl, u32 colour, u32 alpha, s32 shuttertime60, s8 sta gDPSetEnvColorViaWord(gdl++, (colour & 0xffffff00) | (spec & 0xff)); tmp = bview0f142d74(s2, f26, halfheight, sqhalfheight) * startupfrac; +#ifdef PLATFORM_N64 gdl = bviewCopyPixels(gdl, fb, i, 5, i, tmp, viewleft, viewwidth); +#else + gdl = bviewDrawFisheyeLine(gdl, viewleft, viewwidth, i, tmp); +#endif } } @@ -681,13 +756,21 @@ Gfx *bviewDrawFisheye(Gfx *gdl, u32 colour, u32 alpha, s32 shuttertime60, s8 sta } tmp = bview0f142d74(s2, f26, halfheight, sqhalfheight) * f22; +#ifdef PLATFORM_N64 gdl = bviewCopyPixels(gdl, fb, i, 5, i, tmp, viewleft, viewwidth); +#else + gdl = bviewDrawFisheyeLine(gdl, viewleft, viewwidth, i, tmp); +#endif if (hit == EYESPYHIT_DAMAGE) { gDPSetEnvColorViaWord(gdl++, 0xddaaaa99); tmp = bview0f142d74(s2, f26, halfheight, sqhalfheight) * 1.03f; +#ifdef PLATFORM_N64 gdl = bviewCopyPixels(gdl, fb, i, 5, i, tmp, viewleft, viewwidth); +#else + gdl = bviewDrawFisheyeLine(gdl, viewleft, viewwidth, i, tmp); +#endif } s2 += s3; diff --git a/src/game/chr.c b/src/game/chr.c index 63e5aa7dd..c037e9a3a 100644 --- a/src/game/chr.c +++ b/src/game/chr.c @@ -6426,7 +6426,7 @@ Gfx *chrRenderCloak(Gfx *gdl, struct prop *chrprop, struct prop *thisprop) gDPPipeSync(gdl++); gSPTextureRectangle(gdl++, 0, 0, 60, 60, G_TX_RENDERTILE, 0, 0, 4096, 1024); #else - gDPCopyFramebufferEXT(gdl++, var8009ccc0[index], 0, uls, ult); + gDPCopyFramebufferEXT(gdl++, var8009ccc0[index], 0, uls, ult, G_ON); 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, diff --git a/src/game/chrmgr.c b/src/game/chrmgr.c index 94ec7fb96..0d3a0232f 100644 --- a/src/game/chrmgr.c +++ b/src/game/chrmgr.c @@ -41,7 +41,7 @@ void chrmgrReset(void) var8009ccc0[i] = (void *)ALIGN64(mempAlloc(16 * 16 * sizeof(u16) + 0x40, MEMPOOL_STAGE)); #else if (!var8009ccc0[i]) { - var8009ccc0[i] = videoCreateFramebuffer(16, 16); + var8009ccc0[i] = videoCreateFramebuffer(16, 16, false, false); } #endif } diff --git a/src/game/menugfx.c b/src/game/menugfx.c index a73da3943..833f9a670 100644 --- a/src/game/menugfx.c +++ b/src/game/menugfx.c @@ -123,7 +123,7 @@ void menugfxCreateBlur(void) g_ScaleX = 1; #else if (g_MenuBlurFb == 0) { - g_MenuBlurFb = videoCreateFramebuffer(BLURIMG_WIDTH, BLURIMG_HEIGHT); + g_MenuBlurFb = videoCreateFramebuffer(BLURIMG_WIDTH, BLURIMG_HEIGHT, true, false); } // copy full viewport and downscale to 40x30 videoCopyFramebuffer(g_MenuBlurFb, 0, -1, -1); diff --git a/src/include/data.h b/src/include/data.h index 26613a80f..87b0944eb 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -546,6 +546,8 @@ extern f32 g_ViShakeIntensityMult; extern u32 g_TexFilter2D; extern u32 g_HudAlignModeL; extern u32 g_HudAlignModeR; +extern s32 g_PrevFrameFb; +extern s32 g_PrevFrameCapTimer; #define TEX_FILTER_2D g_TexFilter2D #define ADJUST_ZOOM_FOV(x) ((x) * g_PlayerFovZoomMultiplier) diff --git a/src/include/gbiex.h b/src/include/gbiex.h index c3bf9e964..83575cdc6 100644 --- a/src/include/gbiex.h +++ b/src/include/gbiex.h @@ -193,6 +193,7 @@ #define G_EXTRAGEOMETRYMODE_EXT 0x3a #define G_SETINTENSITY_EXT 0x40 #define G_COPYFB_EXT 0x41 +#define G_IMAGERECT_EXT 0x42 /* G_EXTRAGEOMETRYMODE flags */ @@ -212,12 +213,13 @@ #define gDPSetFramebufferTextureEXT(pkt, f, s, w, i) \ gSetImage(pkt, G_SETTIMG_FB_EXT, f, s, w, i) -#define gDPCopyFramebufferEXT(pkt, dst, src, uls, ult) \ +#define gDPCopyFramebufferEXT(pkt, dst, src, uls, ult, back) \ { \ Gfx *_g = (Gfx *)(pkt); \ \ _g->words.w0 = _SHIFTL(G_COPYFB_EXT, 24, 8) \ - | _SHIFTL(dst, 12, 12) | _SHIFTL(src, 0, 12); \ + | _SHIFTL(dst, 11, 11) | _SHIFTL(src, 0, 11) | \ + _SHIFTL(back, 22, 1); \ _g->words.w1 = _SHIFTL(uls, 16, 16) | _SHIFTL(ult, 0, 16); \ } @@ -251,19 +253,31 @@ _g1->words.w1 = _SHIFTL((uly), 2, 22); \ } -#define gSPTextureRectangleWideEXT(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +#define gSPTextureRectangleWideEXT(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy, flip) \ +{ \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_TEXRECT_WIDE_EXT, 24, 8) | _SHIFTL((xh), 0, 24); \ + _g0->words.w1 = (_SHIFTL((yh), 0, 24) | _SHIFTL((tile), 24, 3) | _SHIFTL((flip), 27, 1)); \ + _g1->words.w0 = _SHIFTL((xl), 0, 24); \ + _g1->words.w1 = _SHIFTL((yl), 0, 24); \ + _g2->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ +} + +#define gSPImageRectangleEXT(pkt, x0, y0, s0, t0, x1, y1, s1, t1, tile, iw, ih) \ { \ Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt); \ \ - _g0->words.w0 = _SHIFTL(G_TEXRECT_WIDE_EXT, 24, 8) | _SHIFTL((xh), 0, 24); \ - _g0->words.w1 = _SHIFTL((yh), 0, 24); \ - _g1->words.w0 = (_SHIFTL(tile, 24, 3) | _SHIFTL((xl), 0, 24)); \ - _g1->words.w1 = _SHIFTL((yl), 0, 24); \ - _g2->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ - _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ + _g0->words.w0 = _SHIFTL(G_IMAGERECT_EXT, 24, 8) | _SHIFTL((tile), 0, 3); \ + _g0->words.w1 = _SHIFTL((iw), 16, 16) | _SHIFTL((ih), 0, 16); \ + _g1->words.w0 = _SHIFTL((x0), 16, 16) | _SHIFTL((y0), 0, 16); \ + _g1->words.w1 = _SHIFTL((s0), 16, 16) | _SHIFTL((t0), 0, 16); \ + _g2->words.w0 = _SHIFTL((x1), 16, 16) | _SHIFTL((y1), 0, 16); \ + _g2->words.w1 = _SHIFTL((s1), 16, 16) | _SHIFTL((t1), 0, 16); \ } -#define gSPExtraGeometryModeEXT(pkt, c, s) \ +#define gSPExtraGeometryModeEXT(pkt, c, s) \ { \ Gfx* _g = (Gfx*)(pkt); \ \ @@ -275,7 +289,8 @@ #define gSPClearExtraGeometryModeEXT(pkt, word) gSPExtraGeometryModeEXT((pkt), word, 0) #define gDPFillRectangleEXT gDPFillRectangleWideEXT -#define gSPTextureRectangleEXT gSPTextureRectangleWideEXT +#define gSPTextureRectangleEXT(p, xl, yl, xh, yh, tile, s, t, ds, dt) gSPTextureRectangleWideEXT(p, xl, yl, xh, yh, tile, s, t, ds, dt, G_OFF) +#define gSPTextureRectangleFlipEXT(p, xl, yl, xh, yh, tile, s, t, ds, dt) gSPTextureRectangleWideEXT(p, xl, yl, xh, yh, tile, s, t, ds, dt, G_ON) #undef gDPFillRectangleScaled #define gDPFillRectangleScaled(pkt, x1, y1, x2, y2) gDPFillRectangleEXT(pkt, (x1) * g_ScaleX, y1, (x2) * g_ScaleX, y2)