Implement jumbotron framebuffer effect (#121)

This commit is contained in:
Archez
2024-11-14 22:33:51 -05:00
committed by GitHub
parent 895901d9c6
commit 386bd126e2
7 changed files with 111 additions and 55 deletions
+5
View File
@@ -141,6 +141,7 @@ struct SPTask* gGfxSPTask;
s32 D_801502A0;
s32 D_801502A4;
u16* gPhysicalFramebuffers[3];
u16 gPortFramebuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT];
uintptr_t gPhysicalZBuffer;
UNUSED u32 D_801502B8;
UNUSED u32 D_801502BC;
@@ -910,6 +911,10 @@ void race_logic_loop(void) {
#if DVDL
display_dvdl();
#endif
// Copies after all the main rendering is complete. This is accurate to hardware.
// This means things like jumbotron will have the pause menu rendered in it.
// To avoid that, this line can be moved above func_800591B4.
FB_WriteFramebufferSliceToCPU(&gDisplayListHead, gPortFramebuffers[sRenderingFramebuffer], true);
gDPFullSync(gDisplayListHead++);
gSPEndDisplayList(gDisplayListHead++);
}
+1
View File
@@ -182,6 +182,7 @@ extern struct SPTask* gGfxSPTask;
extern s32 D_801502A0;
extern s32 D_801502A4;
extern u16* gPhysicalFramebuffers[];
extern u16 gPortFramebuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT];
extern uintptr_t gPhysicalZBuffer;
extern Mat4 D_801502C0;
+19 -44
View File
@@ -8,8 +8,6 @@
int gfx_create_framebuffer(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height,
uint8_t resize);
s32 gPauseFrameBuffer = -1;
s32 gBlurFrameBuffer = -1;
// A framebuffer that should only be used for drawing in the same frame that it is copied too
// i.e. the VisMono and VisFbuf effects
s32 gReusableFrameBuffer = -1;
@@ -18,14 +16,6 @@ s32 gReusableFrameBuffer = -1;
s32 gN64ResFrameBuffer = -1;
void FB_CreateFramebuffers(void) {
if (gPauseFrameBuffer == -1) {
gPauseFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true);
}
if (gBlurFrameBuffer == -1) {
gBlurFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true);
}
if (gReusableFrameBuffer == -1) {
gReusableFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true);
}
@@ -35,30 +25,6 @@ void FB_CreateFramebuffers(void) {
}
}
// Fixed point macros
#define FTOFIX(f) ((s32) ((f) * 65536.0))
#define ITOFIX(i) ((s32) ((i) << 16))
#define FIXTOF(x) ((double) ((x) / 65536.0))
#define FIXTOI(x) ((s32) ((x) >> 16))
#define toFixedInt(f) (FTOFIX(f) >> 16)
#define toFrac(f) (FTOFIX(f) & 0xFFFF)
// Setup a fixed-point matrix using floats or doubles. Recommend using doubles for more precision.
#define toFixedPointMatrix(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16) \
{ \
{ ((toFixedInt(x1)) << 16) | toFixedInt(x2), ((toFixedInt(x3)) << 16) | toFixedInt(x4), \
(toFixedInt(x5) << 16) | toFixedInt(x6), (toFixedInt(x7) << 16) | toFixedInt(x8) }, \
{ ((toFixedInt(x9)) << 16) | toFixedInt(x10), ((toFixedInt(x11)) << 16) | toFixedInt(x12), \
(toFixedInt(x13) << 16) | toFixedInt(x14), (toFixedInt(x15) << 16) | toFixedInt(x16) }, \
{ ((toFrac(x1)) << 16) | toFrac(x2), ((toFrac(x3)) << 16) | toFrac(x4), (toFrac(x5) << 16) | toFrac(x6), \
(toFrac(x7) << 16) | toFrac(x8) }, \
{ \
((toFrac(x9)) << 16) | toFrac(x10), ((toFrac(x11)) << 16) | toFrac(x12), \
(toFrac(x13) << 16) | toFrac(x14), (toFrac(x15) << 16) | toFrac(x16) \
} \
}
/**
* Copies the current texture data from the source frame buffer to the destination frame buffer
* Setting oncePerFrame ensures that the copy will only happen once every game frame. This
@@ -67,7 +33,8 @@ void FB_CreateFramebuffers(void) {
* This function uses opcodes from f3dex2 but may be called when s2dex is loaded, such as during shrink window. Make
* sure f3dex2 is loaded before this function is called.
*/
void FB_CopyToFramebuffer(Gfx* gfx, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied) {
void FB_CopyToFramebuffer(Gfx** gfxP, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied) {
Gfx* gfx = *gfxP;
gSPMatrix(gfx++, LOAD_ASSET(D_0D008E98), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
@@ -89,6 +56,8 @@ void FB_CopyToFramebuffer(Gfx* gfx, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8
gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
gDPCopyFB(gfx++, fb_dest, fb_src, oncePerFrame, hasCopied);
*gfxP = gfx;
}
/**
@@ -97,10 +66,10 @@ void FB_CopyToFramebuffer(Gfx* gfx, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8
* Specify the byteswap flag to force the buffer data to be written as BigEndian, which is
* required if the buffer is being used as a texture in F3D.
*/
void FB_WriteFramebufferSliceToCPU(Gfx* gfx, void* buffer, u8 byteSwap) {
// Gfx* gfx = *gfxp;
printf("write!\n");
FB_CopyToFramebuffer(gfx, 0, gReusableFrameBuffer, false, NULL);
void FB_WriteFramebufferSliceToCPU(Gfx** gfxP, void* buffer, u8 byteSwap) {
Gfx* gfx = *gfxP;
FB_CopyToFramebuffer(&gfx, 0, gReusableFrameBuffer, false, NULL);
// Set the N64 resolution framebuffer as the draw target (320x240)
gsSPSetFB(gfx++, gN64ResFrameBuffer);
@@ -110,7 +79,6 @@ void FB_WriteFramebufferSliceToCPU(Gfx* gfx, void* buffer, u8 byteSwap) {
int16_t s0 = 0, t0 = 0;
int16_t s1 = OTRGetGameRenderWidth();
int16_t t1 = OTRGetGameRenderHeight();
printf("half!\n");
float aspectRatio = OTRGetAspectRatio();
float fourByThree = 4.0f / 3.0f;
@@ -121,7 +89,7 @@ void FB_WriteFramebufferSliceToCPU(Gfx* gfx, void* buffer, u8 byteSwap) {
s0 = (OTRGetGameRenderWidth() - adjustedWidth) / 2;
s1 -= s0;
}
printf("Aspect!\n");
gDPSetTextureImageFB(gfx++, 0, 0, 0, gReusableFrameBuffer);
gDPImageRectangle(gfx++, 0 << 2, 0 << 2, s0, t0, SCREEN_WIDTH << 2, SCREEN_HEIGHT << 2, s1, t1, G_TX_RENDERTILE,
OTRGetGameRenderWidth(), OTRGetGameRenderHeight());
@@ -132,13 +100,15 @@ void FB_WriteFramebufferSliceToCPU(Gfx* gfx, void* buffer, u8 byteSwap) {
gsSPResetFB(gfx++);
// Reset scissor for original framebuffer
gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
printf("Complete!\n");
*gfxP = gfx;
}
/**
* Draws the texture data from the specified frame buffer as a full screen image
*/
void FB_DrawFromFramebuffer(Gfx* gfx, s32 fb, u8 alpha) {
void FB_DrawFromFramebuffer(Gfx** gfxP, s32 fb, u8 alpha) {
Gfx* gfx = *gfxP;
gSPMatrix(gfx++, LOAD_ASSET(D_0D008E98), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
@@ -161,6 +131,8 @@ void FB_DrawFromFramebuffer(Gfx* gfx, s32 fb, u8 alpha) {
gDPImageRectangle(gfx++, OTRGetRectDimensionFromLeftEdge(0) << 2, 0 << 2, 0, 0,
OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH) << 2, SCREEN_HEIGHT << 2, OTRGetGameRenderWidth(),
OTRGetGameRenderHeight(), G_TX_RENDERTILE, OTRGetGameRenderWidth(), OTRGetGameRenderHeight());
*gfxP = gfx;
}
/**
@@ -168,7 +140,8 @@ void FB_DrawFromFramebuffer(Gfx* gfx, s32 fb, u8 alpha) {
* This function uses opcodes from f3dex2 but may be called when s2dex is loaded, such as during shrink window. Make
* sure f3dex2 is loaded before this function is called.
*/
void FB_DrawFromFramebufferScaled(Gfx* gfx, s32 fb, u8 alpha, float scaleX, float scaleY) {
void FB_DrawFromFramebufferScaled(Gfx** gfxP, s32 fb, u8 alpha, float scaleX, float scaleY) {
Gfx* gfx = *gfxP;
gDPSetEnvColor(gfx++, 255, 255, 255, alpha);
@@ -191,4 +164,6 @@ void FB_DrawFromFramebufferScaled(Gfx* gfx, s32 fb, u8 alpha, float scaleX, floa
OTRGetRectDimensionFromRightEdge((float) (SCREEN_WIDTH - x0)) << 2,
(int) ((float) (SCREEN_HEIGHT - y0)) << 2, OTRGetGameRenderWidth(), OTRGetGameRenderHeight(),
G_TX_RENDERTILE, OTRGetGameRenderWidth(), OTRGetGameRenderHeight());
*gfxP = gfx;
}
+4 -6
View File
@@ -3,15 +3,13 @@
#include <libultraship.h>
extern s32 gPauseFrameBuffer;
extern s32 gBlurFrameBuffer;
extern s32 gReusableFrameBuffer;
extern s32 gN64ResFrameBuffer;
void FB_CreateFramebuffers(void);
void FB_CopyToFramebuffer(Gfx* gfx, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied);
void FB_WriteFramebufferSliceToCPU(Gfx* gfx, void* buffer, u8 byteSwap);
void FB_DrawFromFramebuffer(Gfx* gfx, s32 fb, u8 alpha);
void FB_DrawFromFramebufferScaled(Gfx* gfx, s32 fb, u8 alpha, float scaleX, float scaleY);
void FB_CopyToFramebuffer(Gfx** gfxP, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied);
void FB_WriteFramebufferSliceToCPU(Gfx** gfxP, void* buffer, u8 byteSwap);
void FB_DrawFromFramebuffer(Gfx** gfxP, s32 fb, u8 alpha);
void FB_DrawFromFramebufferScaled(Gfx** gfxP, s32 fb, u8 alpha, float scaleX, float scaleY);
#endif // FRAMEBUFFER_EFFECTS_H
+28 -5
View File
@@ -874,6 +874,16 @@ void render_luigi_raceway(struct UnkStruct_800DC5EC* arg0) {
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
// Invalidate Jumbotron textures so they update each frame
// This could be more efficient if we exposed the non-opcode based invalidation to be called
// inside copy_framebuffers_port
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0xF800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x10800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x11800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x12800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x13800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x14800);
if (func_80290C20(arg0->camera) == 1) {
gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE);
gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
@@ -911,11 +921,6 @@ void render_luigi_raceway(struct UnkStruct_800DC5EC* arg0) {
currentScreenSection = 0;
}
u16* fb = (u16*) gSegmentTable[5] + 0xF800;
// FB_WriteFramebufferSliceToCPU(gDisplayListHead, fb, true);
// FB_DrawFromFramebuffer(gDisplayListHead, 0, fb, true);
// FB_CopyToFramebuffer(gDisplayListHead, 0, fb, false, NULL);
/**
* The jumbo television screen is split into six sections each section is copied one at a time.
* This is done to fit within the n64's texture size requirements; 64x32
@@ -964,6 +969,10 @@ void render_luigi_raceway(struct UnkStruct_800DC5EC* arg0) {
#endif
break;
}
copy_jumbotron_fb_port(D_800DC5DC, D_800DC5E0, currentScreenSection,
(u16*) PHYSICAL_TO_VIRTUAL(gPortFramebuffers[prevFrame]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0xF800));
}
}
@@ -1127,6 +1136,16 @@ void render_wario_stadium(struct UnkStruct_800DC5EC* arg0) {
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
// Invalidate Jumbotron textures so they update each frame
// This could be more efficient if we exposed the non-opcode based invalidation to be called
// inside copy_framebuffers_port
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x8800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0x9800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0xA800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0xB800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0xC800);
gSPInvalidateTexCache(gDisplayListHead++, gSegmentTable[5] + 0xD800);
if (func_80290C20(arg0->camera) == 1) {
gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE);
@@ -1205,6 +1224,10 @@ void render_wario_stadium(struct UnkStruct_800DC5EC* arg0) {
#endif
break;
}
copy_jumbotron_fb_port(D_800DC5DC, D_800DC5E0, currentScreenSection,
(u16*) PHYSICAL_TO_VIRTUAL(gPortFramebuffers[prevFrame]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0x8800));
}
}
+53
View File
@@ -1423,6 +1423,49 @@ void copy_framebuffer(s32 arg0, s32 arg1, s32 width, s32 height, u16* source, u1
}
}
// Handles copying framebuffer to course texture data region for the Jumbotron in the port
// This only supports framebuffers and copy regions being within 320x240
// Any attempt to support larger sizes would require reworking course data
void copy_jumbotron_fb_port(s32 ulx, s32 uly, s16 portionToDraw, u16* source, u16* target) {
// Add CVar if we want to expose a user toggle for only updating 1/6 of the jumbotron per frame
u8 updateWholeJumbo = true;
ulx = 0;
uly = 0;
if (portionToDraw == -1 || updateWholeJumbo) {
copy_framebuffer(ulx, uly, 64, 32, source, target);
copy_framebuffer(ulx + 64, uly, 64, 32, source, target + (64 * 32 * 1));
copy_framebuffer(ulx, uly + 32, 64, 32, source, target + (64 * 32 * 2));
copy_framebuffer(ulx + 64, uly + 32, 64, 32, source, target + (64 * 32 * 3));
copy_framebuffer(ulx, uly + 64, 64, 32, source, target + (64 * 32 * 4));
copy_framebuffer(ulx + 64, uly + 64, 64, 32, source, target + (64 * 32 * 5));
} else {
switch (portionToDraw) {
case 0:
copy_framebuffer(ulx, uly, 64, 32, source, target);
break;
case 1:
copy_framebuffer(ulx + 64, uly, 64, 32, source, target + (64 * 32 * 1));
break;
case 2:
copy_framebuffer(ulx, uly + 32, 64, 32, source, target + (64 * 32 * 2));
break;
case 3:
copy_framebuffer(ulx + 64, uly + 32, 64, 32, source, target + (64 * 32 * 3));
break;
case 4:
copy_framebuffer(ulx, uly + 64, 64, 32, source, target + (64 * 32 * 4));
break;
case 5:
copy_framebuffer(ulx + 64, uly + 64, 64, 32, source, target + (64 * 32 * 5));
break;
default:
break;
}
}
}
void func_802A7728(void) {
s16 temp_v0;
@@ -1438,6 +1481,7 @@ void func_802A7728(void) {
} else if (temp_v0 > 2) {
temp_v0 = 0;
}
#if TARGET_N64
copy_framebuffer(D_800DC5DC, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0x8800));
copy_framebuffer(D_800DC5DC + 64, D_800DC5E0, 64, 32, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]),
@@ -1452,6 +1496,10 @@ void func_802A7728(void) {
copy_framebuffer(D_800DC5DC + 64, D_800DC5E0 + 64, 64, 32,
(u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0xD800));
#else
copy_jumbotron_fb_port(D_800DC5DC, D_800DC5E0, -1, PHYSICAL_TO_VIRTUAL(gPortFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0x8800));
#endif
}
void func_802A7940(void) {
@@ -1469,6 +1517,7 @@ void func_802A7940(void) {
} else if (temp_v0 > 2) {
temp_v0 = 0;
}
#if TARGET_N64
copy_framebuffer(D_800DC5DC, D_800DC5E0, 0x40, 0x20, (u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0xF800));
copy_framebuffer(D_800DC5DC + 0x40, D_800DC5E0, 0x40, 0x20,
@@ -1486,4 +1535,8 @@ void func_802A7940(void) {
copy_framebuffer(D_800DC5DC + 0x40, D_800DC5E0 + 0x40, 0x40, 0x20,
(u16*) PHYSICAL_TO_VIRTUAL(gPhysicalFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0x14800));
#else
copy_jumbotron_fb_port(D_800DC5DC, D_800DC5E0, -1, (u16*) PHYSICAL_TO_VIRTUAL(gPortFramebuffers[temp_v0]),
(u16*) PHYSICAL_TO_VIRTUAL(gSegmentTable[5] + 0xF800));
#endif
}
+1
View File
@@ -53,6 +53,7 @@ void render_player_three_3p_4p_screen(void);
void render_player_four_3p_4p_screen(void);
void func_802A74BC(void);
void copy_framebuffer(s32, s32, s32, s32, u16*, u16*);
void copy_jumbotron_fb_port(s32 ulx, s32 uly, s16 portionToDraw, u16* source, u16* target);
void func_802A7728(void);
void func_802A7940(void);