2017-02-09 04:30:25 +01:00
|
|
|
#include "internal.h"
|
2017-02-14 18:34:14 +01:00
|
|
|
#include <c3d/base.h>
|
2016-01-05 17:30:05 +01:00
|
|
|
#include <c3d/renderqueue.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
static C3D_RenderTarget *firstTarget, *lastTarget;
|
|
|
|
static C3D_RenderTarget *linkedTarget[3];
|
|
|
|
|
2017-02-16 14:39:48 +01:00
|
|
|
static TickCounter gpuTime, cpuTime;
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
static bool inFrame, inSafeTransfer, measureGpuTime;
|
2020-07-07 13:08:56 +02:00
|
|
|
static bool needSwapTop, needSwapBot, isTopStereo;
|
2017-02-09 19:57:02 +01:00
|
|
|
static float framerate = 60.0f;
|
|
|
|
static float framerateCounter[2] = { 60.0f, 60.0f };
|
2017-03-26 20:15:01 +02:00
|
|
|
static u32 frameCounter[2];
|
2017-10-30 21:06:23 +01:00
|
|
|
static void (* frameEndCb)(void*);
|
|
|
|
static void* frameEndCbData;
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2020-05-05 16:57:18 +02:00
|
|
|
static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target);
|
|
|
|
|
2017-02-09 19:57:02 +01:00
|
|
|
static bool framerateLimit(int id)
|
|
|
|
{
|
|
|
|
framerateCounter[id] -= framerate;
|
|
|
|
if (framerateCounter[id] <= 0.0f)
|
|
|
|
{
|
|
|
|
framerateCounter[id] += 60.0f;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-02-09 04:30:25 +01:00
|
|
|
static void onVBlank0(C3D_UNUSED void* unused)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
if (framerateLimit(0))
|
|
|
|
frameCounter[0]++;
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2017-02-09 04:30:25 +01:00
|
|
|
static void onVBlank1(C3D_UNUSED void* unused)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
if (framerateLimit(1))
|
|
|
|
frameCounter[1]++;
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
static void onQueueFinish(gxCmdQueue_s* queue)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
if (measureGpuTime)
|
|
|
|
{
|
|
|
|
osTickCounterUpdate(&gpuTime);
|
|
|
|
measureGpuTime = false;
|
|
|
|
}
|
2016-03-26 00:30:43 +01:00
|
|
|
if (inSafeTransfer)
|
|
|
|
{
|
|
|
|
inSafeTransfer = false;
|
2017-03-26 20:15:01 +02:00
|
|
|
if (inFrame)
|
|
|
|
{
|
|
|
|
gxCmdQueueStop(queue);
|
|
|
|
gxCmdQueueClear(queue);
|
|
|
|
}
|
2016-03-26 00:30:43 +01:00
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
else
|
2017-03-26 20:15:01 +02:00
|
|
|
{
|
2020-05-04 22:23:49 +02:00
|
|
|
if (needSwapTop)
|
|
|
|
{
|
2020-07-07 13:08:56 +02:00
|
|
|
gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
|
2020-05-04 22:23:49 +02:00
|
|
|
needSwapTop = false;
|
|
|
|
}
|
|
|
|
if (needSwapBot)
|
|
|
|
{
|
2020-07-07 13:08:56 +02:00
|
|
|
gfxScreenSwapBuffers(GFX_BOTTOM, false);
|
2020-05-04 22:23:49 +02:00
|
|
|
needSwapBot = false;
|
|
|
|
}
|
2017-03-26 20:15:01 +02:00
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
void C3D_FrameSync(void)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
u32 cur[2];
|
|
|
|
u32 start[2] = { frameCounter[0], frameCounter[1] };
|
|
|
|
do
|
2016-03-26 00:30:43 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
gspWaitForAnyEvent();
|
|
|
|
cur[0] = frameCounter[0];
|
|
|
|
cur[1] = frameCounter[1];
|
|
|
|
} while (cur[0]==start[0] || cur[1]==start[1]);
|
|
|
|
}
|
|
|
|
|
2017-05-14 23:09:56 +02:00
|
|
|
u32 C3D_FrameCounter(int id)
|
|
|
|
{
|
|
|
|
return frameCounter[id];
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
static bool C3Di_WaitAndClearQueue(s64 timeout)
|
|
|
|
{
|
|
|
|
gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue;
|
|
|
|
if (!gxCmdQueueWait(queue, timeout))
|
|
|
|
return false;
|
|
|
|
gxCmdQueueStop(queue);
|
|
|
|
gxCmdQueueClear(queue);
|
|
|
|
return true;
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 20:21:03 +02:00
|
|
|
void C3Di_RenderQueueEnableVBlank(void)
|
|
|
|
{
|
|
|
|
gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
|
|
|
|
gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void C3Di_RenderQueueDisableVBlank(void)
|
|
|
|
{
|
|
|
|
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
|
|
|
|
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
|
|
|
|
}
|
|
|
|
|
2020-05-05 16:57:18 +02:00
|
|
|
void C3Di_RenderQueueInit(void)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2020-05-05 16:57:18 +02:00
|
|
|
C3D_Context* ctx = C3Di_GetContext();
|
|
|
|
|
2020-07-06 20:21:03 +02:00
|
|
|
C3Di_RenderQueueEnableVBlank();
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2020-05-05 16:57:18 +02:00
|
|
|
GX_BindQueue(&ctx->gxQueue);
|
|
|
|
gxCmdQueueSetCallback(&ctx->gxQueue, onQueueFinish, NULL);
|
|
|
|
gxCmdQueueRun(&ctx->gxQueue);
|
|
|
|
}
|
2017-03-26 20:15:01 +02:00
|
|
|
|
2017-03-05 23:28:30 +01:00
|
|
|
void C3Di_RenderQueueExit(void)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
C3D_RenderTarget *a, *next;
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_WaitAndClearQueue(-1);
|
2020-05-05 16:57:18 +02:00
|
|
|
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
|
|
|
|
GX_BindQueue(NULL);
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2020-07-06 20:21:03 +02:00
|
|
|
C3Di_RenderQueueDisableVBlank();
|
2016-01-05 17:30:05 +01:00
|
|
|
|
|
|
|
for (i = 0; i < 3; i ++)
|
|
|
|
linkedTarget[i] = NULL;
|
|
|
|
|
2020-05-05 16:57:18 +02:00
|
|
|
for (a = firstTarget; a; a = next)
|
|
|
|
{
|
|
|
|
next = a->next;
|
|
|
|
C3Di_RenderTargetDestroy(a);
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2017-03-05 23:28:30 +01:00
|
|
|
void C3Di_RenderQueueWaitDone(void)
|
2016-01-22 00:06:15 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_WaitAndClearQueue(-1);
|
2016-01-22 00:06:15 +01:00
|
|
|
}
|
|
|
|
|
2017-02-09 19:57:02 +01:00
|
|
|
float C3D_FrameRate(float fps)
|
|
|
|
{
|
|
|
|
float old = framerate;
|
|
|
|
if (fps > 0.0f && fps <= 60.0f)
|
|
|
|
{
|
|
|
|
framerate = fps;
|
|
|
|
framerateCounter[0] = fps;
|
|
|
|
framerateCounter[1] = fps;
|
|
|
|
}
|
|
|
|
return old;
|
|
|
|
}
|
|
|
|
|
2016-01-05 17:30:05 +01:00
|
|
|
bool C3D_FrameBegin(u8 flags)
|
|
|
|
{
|
2020-05-05 16:57:18 +02:00
|
|
|
C3D_Context* ctx = C3Di_GetContext();
|
2016-01-05 17:30:05 +01:00
|
|
|
if (inFrame) return false;
|
2020-05-05 16:57:18 +02:00
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
if (flags & C3D_FRAME_SYNCDRAW)
|
|
|
|
C3D_FrameSync();
|
|
|
|
if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1))
|
|
|
|
return false;
|
2020-05-05 16:57:18 +02:00
|
|
|
|
2016-01-05 17:30:05 +01:00
|
|
|
inFrame = true;
|
2017-03-26 20:15:01 +02:00
|
|
|
osTickCounterStart(&cpuTime);
|
2020-05-05 16:57:18 +02:00
|
|
|
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
|
2016-01-05 17:30:05 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool C3D_FrameDrawOn(C3D_RenderTarget* target)
|
|
|
|
{
|
|
|
|
if (!inFrame) return false;
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
target->used = true;
|
2017-02-14 18:34:14 +01:00
|
|
|
C3D_SetFrameBuf(&target->frameBuf);
|
|
|
|
C3D_SetViewport(0, 0, target->frameBuf.width, target->frameBuf.height);
|
2016-01-05 17:30:05 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
void C3D_FrameSplit(u8 flags)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
u32 *cmdBuf, cmdBufSize;
|
2016-01-05 17:30:05 +01:00
|
|
|
if (!inFrame) return;
|
2017-03-26 20:15:01 +02:00
|
|
|
if (C3Di_SplitFrame(&cmdBuf, &cmdBufSize))
|
|
|
|
GX_ProcessCommandList(cmdBuf, cmdBufSize*4, flags);
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
void C3D_FrameEnd(u8 flags)
|
|
|
|
{
|
|
|
|
C3D_Context* ctx = C3Di_GetContext();
|
2020-05-05 16:57:18 +02:00
|
|
|
if (!inFrame) return;
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2017-10-30 21:06:23 +01:00
|
|
|
if (frameEndCb)
|
|
|
|
frameEndCb(frameEndCbData);
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
C3D_FrameSplit(flags);
|
2020-05-05 16:57:18 +02:00
|
|
|
GPUCMD_SetBuffer(NULL, 0, 0);
|
2017-03-26 20:15:01 +02:00
|
|
|
osTickCounterUpdate(&cpuTime);
|
2020-05-05 16:57:18 +02:00
|
|
|
inFrame = false;
|
2016-01-05 17:30:05 +01:00
|
|
|
|
|
|
|
// Flush the entire linear memory if the user did not explicitly mandate to flush the command list
|
|
|
|
if (!(flags & GX_CMDLIST_FLUSH))
|
|
|
|
{
|
|
|
|
extern u32 __ctru_linear_heap;
|
|
|
|
extern u32 __ctru_linear_heap_size;
|
2017-03-26 20:15:01 +02:00
|
|
|
GSPGPU_FlushDataCache((void*)__ctru_linear_heap, __ctru_linear_heap_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
C3D_RenderTarget* target;
|
2020-07-07 13:08:56 +02:00
|
|
|
isTopStereo = false;
|
2017-03-26 20:15:01 +02:00
|
|
|
for (i = 2; i >= 0; i --)
|
|
|
|
{
|
|
|
|
target = linkedTarget[i];
|
|
|
|
if (!target || !target->used)
|
|
|
|
continue;
|
|
|
|
target->used = false;
|
2020-05-04 22:23:49 +02:00
|
|
|
C3D_FrameBufTransfer(&target->frameBuf, target->screen, target->side, target->transferFlags);
|
|
|
|
if (target->screen == GFX_TOP)
|
2020-07-07 13:08:56 +02:00
|
|
|
{
|
2020-05-04 22:23:49 +02:00
|
|
|
needSwapTop = true;
|
2020-07-07 13:08:56 +02:00
|
|
|
if (target->side == GFX_RIGHT)
|
|
|
|
isTopStereo = true;
|
|
|
|
}
|
2020-05-04 22:23:49 +02:00
|
|
|
else if (target->screen == GFX_BOTTOM)
|
|
|
|
needSwapBot = true;
|
2017-03-26 20:15:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
measureGpuTime = true;
|
|
|
|
osTickCounterStart(&gpuTime);
|
|
|
|
gxCmdQueueRun(&ctx->gxQueue);
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
|
|
|
|
2017-10-30 21:06:23 +01:00
|
|
|
void C3D_FrameEndHook(void (* hook)(void*), void* param)
|
|
|
|
{
|
|
|
|
frameEndCb = hook;
|
|
|
|
frameEndCbData = param;
|
|
|
|
}
|
|
|
|
|
2017-02-16 14:39:48 +01:00
|
|
|
float C3D_GetDrawingTime(void)
|
|
|
|
{
|
|
|
|
return osTickCounterRead(&gpuTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
float C3D_GetProcessingTime(void)
|
|
|
|
{
|
|
|
|
return osTickCounterRead(&cpuTime);
|
|
|
|
}
|
|
|
|
|
2017-02-14 18:34:14 +01:00
|
|
|
static C3D_RenderTarget* C3Di_RenderTargetNew(void)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
|
|
|
C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget));
|
|
|
|
if (!target) return NULL;
|
|
|
|
memset(target, 0, sizeof(C3D_RenderTarget));
|
2017-02-14 18:34:14 +01:00
|
|
|
return target;
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2017-02-14 18:34:14 +01:00
|
|
|
static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target)
|
|
|
|
{
|
2016-01-05 17:30:05 +01:00
|
|
|
target->prev = lastTarget;
|
|
|
|
target->next = NULL;
|
|
|
|
if (lastTarget)
|
|
|
|
lastTarget->next = target;
|
|
|
|
if (!firstTarget)
|
|
|
|
firstTarget = target;
|
|
|
|
lastTarget = target;
|
2017-02-14 18:34:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt)
|
|
|
|
{
|
|
|
|
GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16;
|
|
|
|
void* depthBuf = NULL;
|
2017-03-26 20:15:01 +02:00
|
|
|
void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt));
|
2017-02-14 18:34:14 +01:00
|
|
|
if (!colorBuf) goto _fail0;
|
|
|
|
if (C3D_DEPTHTYPE_OK(depthFmt))
|
|
|
|
{
|
|
|
|
depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
|
2021-08-26 23:46:07 +02:00
|
|
|
size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmtReal);
|
|
|
|
vramAllocPos vramBank = addrGetVRAMBank(colorBuf);
|
|
|
|
depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
|
|
|
|
if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
|
2017-02-14 18:34:14 +01:00
|
|
|
if (!depthBuf) goto _fail1;
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
|
2017-02-14 18:34:14 +01:00
|
|
|
C3D_RenderTarget* target = C3Di_RenderTargetNew();
|
|
|
|
if (!target) goto _fail2;
|
|
|
|
|
|
|
|
C3D_FrameBuf* fb = &target->frameBuf;
|
|
|
|
C3D_FrameBufAttrib(fb, width, height, false);
|
|
|
|
C3D_FrameBufColor(fb, colorBuf, colorFmt);
|
|
|
|
target->ownsColor = true;
|
|
|
|
if (depthBuf)
|
|
|
|
{
|
|
|
|
C3D_FrameBufDepth(fb, depthBuf, depthFmtReal);
|
|
|
|
target->ownsDepth = true;
|
|
|
|
}
|
|
|
|
C3Di_RenderTargetFinishInit(target);
|
|
|
|
return target;
|
|
|
|
|
|
|
|
_fail2:
|
|
|
|
if (depthBuf) vramFree(depthBuf);
|
|
|
|
_fail1:
|
|
|
|
vramFree(colorBuf);
|
|
|
|
_fail0:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt)
|
|
|
|
{
|
2021-08-26 23:46:07 +02:00
|
|
|
if (!addrIsVRAM(tex->data)) return NULL; // Render targets must be in VRAM
|
2017-02-14 18:34:14 +01:00
|
|
|
C3D_RenderTarget* target = C3Di_RenderTargetNew();
|
|
|
|
if (!target) return NULL;
|
|
|
|
|
|
|
|
C3D_FrameBuf* fb = &target->frameBuf;
|
|
|
|
C3D_FrameBufTex(fb, tex, face, level);
|
|
|
|
|
|
|
|
if (C3D_DEPTHTYPE_OK(depthFmt))
|
|
|
|
{
|
|
|
|
GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
|
2021-08-26 23:46:07 +02:00
|
|
|
size_t depthSize = C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal);
|
|
|
|
vramAllocPos vramBank = addrGetVRAMBank(tex->data);
|
|
|
|
void* depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
|
|
|
|
if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
|
2017-02-14 18:34:14 +01:00
|
|
|
if (!depthBuf)
|
|
|
|
{
|
|
|
|
free(target);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
C3D_FrameBufDepth(fb, depthBuf, depthFmtReal);
|
|
|
|
target->ownsDepth = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
C3Di_RenderTargetFinishInit(target);
|
2016-01-05 17:30:05 +01:00
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
void C3Di_RenderTargetDestroy(C3D_RenderTarget* target)
|
2016-01-05 17:30:05 +01:00
|
|
|
{
|
2017-02-14 18:34:14 +01:00
|
|
|
if (target->ownsColor)
|
|
|
|
vramFree(target->frameBuf.colorBuf);
|
|
|
|
if (target->ownsDepth)
|
|
|
|
vramFree(target->frameBuf.depthBuf);
|
|
|
|
|
2016-01-05 17:30:05 +01:00
|
|
|
C3D_RenderTarget** prevNext = target->prev ? &target->prev->next : &firstTarget;
|
|
|
|
C3D_RenderTarget** nextPrev = target->next ? &target->next->prev : &lastTarget;
|
|
|
|
*prevNext = target->next;
|
|
|
|
*nextPrev = target->prev;
|
|
|
|
free(target);
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:15:01 +02:00
|
|
|
void C3D_RenderTargetDelete(C3D_RenderTarget* target)
|
|
|
|
{
|
|
|
|
if (inFrame)
|
|
|
|
svcBreak(USERBREAK_PANIC); // Shouldn't happen.
|
2020-09-28 23:33:02 +02:00
|
|
|
if (target->linked)
|
|
|
|
C3D_RenderTargetDetachOutput(target);
|
|
|
|
else
|
|
|
|
C3Di_WaitAndClearQueue(-1);
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_RenderTargetDestroy(target);
|
|
|
|
}
|
|
|
|
|
2016-01-05 17:30:05 +01:00
|
|
|
void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags)
|
|
|
|
{
|
|
|
|
int id = 0;
|
|
|
|
if (screen==GFX_BOTTOM) id = 2;
|
|
|
|
else if (side==GFX_RIGHT) id = 1;
|
|
|
|
if (linkedTarget[id])
|
2020-09-28 23:33:02 +02:00
|
|
|
{
|
2016-01-05 17:30:05 +01:00
|
|
|
linkedTarget[id]->linked = false;
|
2020-09-28 23:33:02 +02:00
|
|
|
if (!inFrame)
|
|
|
|
C3Di_WaitAndClearQueue(-1);
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
linkedTarget[id] = target;
|
2020-09-28 23:33:02 +02:00
|
|
|
if (target)
|
|
|
|
{
|
|
|
|
target->linked = true;
|
|
|
|
target->transferFlags = transferFlags;
|
|
|
|
target->screen = screen;
|
|
|
|
target->side = side;
|
|
|
|
}
|
2016-01-05 17:30:05 +01:00
|
|
|
}
|
2016-03-26 00:30:43 +01:00
|
|
|
|
2017-11-12 14:25:03 +01:00
|
|
|
static void C3Di_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
|
2016-03-26 00:30:43 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_WaitAndClearQueue(-1);
|
2016-03-26 00:30:43 +01:00
|
|
|
inSafeTransfer = true;
|
|
|
|
GX_DisplayTransfer(inadr, indim, outadr, outdim, flags);
|
2017-03-26 20:15:01 +02:00
|
|
|
gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
|
2016-03-26 00:30:43 +01:00
|
|
|
}
|
|
|
|
|
2017-11-12 14:25:03 +01:00
|
|
|
static void C3Di_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
|
2016-03-26 00:30:43 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_WaitAndClearQueue(-1);
|
2016-03-26 00:30:43 +01:00
|
|
|
inSafeTransfer = true;
|
|
|
|
GX_TextureCopy(inadr, indim, outadr, outdim, size, flags);
|
2017-03-26 20:15:01 +02:00
|
|
|
gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
|
2016-03-26 00:30:43 +01:00
|
|
|
}
|
|
|
|
|
2017-11-12 14:25:03 +01:00
|
|
|
static void C3Di_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
|
2016-03-26 00:30:43 +01:00
|
|
|
{
|
2017-03-26 20:15:01 +02:00
|
|
|
C3Di_WaitAndClearQueue(-1);
|
|
|
|
inSafeTransfer = true;
|
2016-03-26 00:30:43 +01:00
|
|
|
GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
|
2017-03-26 20:15:01 +02:00
|
|
|
gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
|
2016-03-26 00:30:43 +01:00
|
|
|
}
|
2017-11-12 14:25:03 +01:00
|
|
|
|
|
|
|
void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
|
|
|
|
{
|
|
|
|
if (inFrame)
|
|
|
|
{
|
|
|
|
C3D_FrameSplit(0);
|
|
|
|
GX_DisplayTransfer(inadr, indim, outadr, outdim, flags);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
C3Di_SafeDisplayTransfer(inadr, indim, outadr, outdim, flags);
|
|
|
|
gspWaitForPPF();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
|
|
|
|
{
|
|
|
|
if (inFrame)
|
|
|
|
{
|
|
|
|
C3D_FrameSplit(0);
|
|
|
|
GX_TextureCopy(inadr, indim, outadr, outdim, size, flags);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
C3Di_SafeTextureCopy(inadr, indim, outadr, outdim, size, flags);
|
|
|
|
gspWaitForPPF();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
|
|
|
|
{
|
|
|
|
if (inFrame)
|
|
|
|
{
|
|
|
|
C3D_FrameSplit(0);
|
|
|
|
GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
C3Di_SafeMemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
|
|
|
|
gspWaitForPSC0();
|
|
|
|
}
|
|
|
|
}
|