3DS: Refactor vblank handling

This commit is contained in:
UnknownShadow200 2025-09-14 07:39:15 +10:00
parent 4003ff7d6b
commit c6fada2c1f
3 changed files with 35 additions and 63 deletions

View File

@ -7,6 +7,7 @@
#define BUFFER_BASE_PADDR OS_VRAM_PADDR // VRAM physical address
#include "../../third_party/citro3d.c"
#include "gsp_gpu.h"
#include "pica_gpu.h"
// See the .v.pica shader files in misc/3ds
#define CONST_MVP 0 // c0-c3
@ -121,9 +122,7 @@ static aptHookCookie hookCookie;
static void AptEventHook(APT_HookType hookType, void* param) {
if (hookType == APTHOOK_ONSUSPEND) {
C3Di_RenderQueueWaitDone();
C3Di_RenderQueueDisableVBlank();
} else if (hookType == APTHOOK_ONRESTORE) {
C3Di_RenderQueueEnableVBlank();
C3Di_OnRestore();
}
}
@ -153,6 +152,8 @@ static void InitCitro3D(void) {
gfxSetDoubleBuffering(GFX_TOP, true);
SetDefaultState();
AllocShaders();
GSP_setup();
}
static GfxResourceID white_square;
@ -547,7 +548,7 @@ void Gfx_SetVSync(cc_bool vsync) {
void Gfx_BeginFrame(void) {
rendering3D = false;
// wait for vblank for both screens TODO move to end?
if (gfx_vsync) C3D_FrameSync();
if (gfx_vsync) GSP_wait_for_full_vblank();
C3D_FrameBegin(0);
topTarget = &topTargetLeft;

25
src/3ds/gsp_gpu.h Normal file
View File

@ -0,0 +1,25 @@
static volatile uint32_t vblanks_0;
static volatile uint32_t vblanks_1;
static void handle_vblank(void* ptr) {
uint32_t* counter = (uint32_t*)ptr;
(*counter)++;
}
static void GSP_setup(void) {
// Start listening for VBLANK events
gspSetEventCallback(GSPGPU_EVENT_VBlank0, handle_vblank, (void*)&vblanks_0, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, handle_vblank, (void*)&vblanks_1, false);
}
// Waits for VBLANK for both top and bottom screens
static void GSP_wait_for_full_vblank(void) {
uint32_t init0 = vblanks_0;
uint32_t init1 = vblanks_1;
for (;;) {
gspWaitForAnyEvent();
if (vblanks_0 != init0 && vblanks_1 != init1) return;
}
}

66
third_party/citro3d.c vendored
View File

@ -245,12 +245,9 @@ struct C3D_RenderTarget_tag
// Flags for C3D_FrameBegin
enum
{
C3D_FRAME_SYNCDRAW = BIT(0), // Perform C3D_FrameSync before checking the GPU status
C3D_FRAME_NONBLOCK = BIT(1), // Return false instead of waiting if the GPU is busy
};
static void C3D_FrameSync(void);
static bool C3D_FrameBegin(u8 flags);
static bool C3D_FrameDrawOn(C3D_RenderTarget* target);
static void C3D_FrameSplit(u8 flags);
@ -404,10 +401,6 @@ static bool C3Di_SplitFrame(u32** pBuf, u32* pSize);
static void C3Di_RenderQueueInit(void);
static void C3Di_RenderQueueExit(void);
static void C3Di_RenderQueueWaitDone(void);
static void C3Di_RenderQueueEnableVBlank(void);
static void C3Di_RenderQueueDisableVBlank(void);
@ -837,21 +830,10 @@ static void C3D_ImmDrawEnd(void)
static C3D_RenderTarget *linkedTarget[3];
static bool inFrame, inSafeTransfer;
static bool needSwapTop, needSwapBot, isTopStereo;
static u32 vblankCounter[2];
static bool swapPending, isTopStereo;
static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target);
static void onVBlank0(void* unused)
{
vblankCounter[0]++;
}
static void onVBlank1(void* unused)
{
vblankCounter[1]++;
}
static void onQueueFinish(gxCmdQueue_s* queue)
{
if (inSafeTransfer)
@ -863,61 +845,28 @@ static void onQueueFinish(gxCmdQueue_s* queue)
gxCmdQueueClear(queue);
}
}
else
else if (swapPending)
{
if (needSwapTop)
{
gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
needSwapTop = false;
}
if (needSwapBot)
{
gfxScreenSwapBuffers(GFX_BOTTOM, false);
needSwapBot = false;
}
gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
gfxScreenSwapBuffers(GFX_BOTTOM, false);
}
}
static void C3D_FrameSync(void)
{
u32 cur[2];
u32 start[2] = { vblankCounter[0], vblankCounter[1] };
do
{
gspWaitForAnyEvent();
cur[0] = vblankCounter[0];
cur[1] = vblankCounter[1];
} while (cur[0]==start[0] || cur[1]==start[1]);
}
static bool C3Di_WaitAndClearQueue(s64 timeout)
{
gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue;
if (!gxCmdQueueWait(queue, timeout))
return false;
gxCmdQueueStop(queue);
gxCmdQueueClear(queue);
return true;
}
static void C3Di_RenderQueueEnableVBlank(void)
{
gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
}
static void C3Di_RenderQueueDisableVBlank(void)
{
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
}
static void C3Di_RenderQueueInit(void)
{
C3D_Context* ctx = C3Di_GetContext();
C3Di_RenderQueueEnableVBlank();
GX_BindQueue(&ctx->gxQueue);
gxCmdQueueSetCallback(&ctx->gxQueue, onQueueFinish, NULL);
gxCmdQueueRun(&ctx->gxQueue);
@ -928,8 +877,6 @@ static void C3Di_RenderQueueExit(void)
C3Di_WaitAndClearQueue(-1);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
GX_BindQueue(NULL);
C3Di_RenderQueueDisableVBlank();
}
static void C3Di_RenderQueueWaitDone(void)
@ -987,8 +934,7 @@ static void C3D_FrameEnd(u8 flags)
C3D_RenderTarget* target;
isTopStereo = false;
needSwapTop = true;
needSwapBot = true;
swapPending = true;
for (int i = 2; i >= 0; i --)
{