Citro3d
Loading...
Searching...
No Matches
renderqueue.c
Go to the documentation of this file.
1#include "internal.h"
2#include <c3d/base.h>
3#include <c3d/renderqueue.h>
4#include <stdlib.h>
5
6static C3D_RenderTarget *firstTarget, *lastTarget;
7static C3D_RenderTarget *linkedTarget[3];
8
9static TickCounter gpuTime, cpuTime;
10
11static bool inFrame, inSafeTransfer, measureGpuTime;
12static bool needSwapTop, needSwapBot, isTopStereo;
13static float framerate = 60.0f;
14static float framerateCounter[2] = { 60.0f, 60.0f };
15static u32 frameCounter[2];
16static void (* frameEndCb)(void*);
17static void* frameEndCbData;
18
19static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target);
20
21static bool framerateLimit(int id)
22{
23 framerateCounter[id] -= framerate;
24 if (framerateCounter[id] <= 0.0f)
25 {
26 framerateCounter[id] += 60.0f;
27 return true;
28 }
29 return false;
30}
31
32static void onVBlank0(C3D_UNUSED void* unused)
33{
34 if (framerateLimit(0))
35 frameCounter[0]++;
36}
37
38static void onVBlank1(C3D_UNUSED void* unused)
39{
40 if (framerateLimit(1))
41 frameCounter[1]++;
42}
43
44static void onQueueFinish(gxCmdQueue_s* queue)
45{
46 if (measureGpuTime)
47 {
48 osTickCounterUpdate(&gpuTime);
49 measureGpuTime = false;
50 }
51 if (inSafeTransfer)
52 {
53 inSafeTransfer = false;
54 if (inFrame)
55 {
56 gxCmdQueueStop(queue);
57 gxCmdQueueClear(queue);
58 }
59 }
60 else
61 {
62 if (needSwapTop)
63 {
64 gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
65 needSwapTop = false;
66 }
67 if (needSwapBot)
68 {
69 gfxScreenSwapBuffers(GFX_BOTTOM, false);
70 needSwapBot = false;
71 }
72 }
73}
74
75void C3D_FrameSync(void)
76{
77 u32 cur[2];
78 u32 start[2] = { frameCounter[0], frameCounter[1] };
79 do
80 {
81 gspWaitForAnyEvent();
82 cur[0] = frameCounter[0];
83 cur[1] = frameCounter[1];
84 } while (cur[0]==start[0] || cur[1]==start[1]);
85}
86
88{
89 return frameCounter[id];
90}
91
92static bool C3Di_WaitAndClearQueue(s64 timeout)
93{
94 gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue;
95 if (!gxCmdQueueWait(queue, timeout))
96 return false;
97 gxCmdQueueStop(queue);
98 gxCmdQueueClear(queue);
99 return true;
100}
101
103{
104 gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
105 gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
106}
107
109{
110 gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
111 gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
112}
113
115{
116 C3D_Context* ctx = C3Di_GetContext();
117
119
120 GX_BindQueue(&ctx->gxQueue);
121 gxCmdQueueSetCallback(&ctx->gxQueue, onQueueFinish, NULL);
122 gxCmdQueueRun(&ctx->gxQueue);
123}
124
126{
127 int i;
128 C3D_RenderTarget *a, *next;
129
130 C3Di_WaitAndClearQueue(-1);
131 gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
132 GX_BindQueue(NULL);
133
135
136 for (i = 0; i < 3; i ++)
137 linkedTarget[i] = NULL;
138
139 for (a = firstTarget; a; a = next)
140 {
141 next = a->next;
142 C3Di_RenderTargetDestroy(a);
143 }
144}
145
147{
148 C3Di_WaitAndClearQueue(-1);
149}
150
151float C3D_FrameRate(float fps)
152{
153 float old = framerate;
154 if (fps > 0.0f && fps <= 60.0f)
155 {
156 framerate = fps;
157 framerateCounter[0] = fps;
158 framerateCounter[1] = fps;
159 }
160 return old;
161}
162
163bool C3D_FrameBegin(u8 flags)
164{
165 C3D_Context* ctx = C3Di_GetContext();
166 if (inFrame) return false;
167
168 if (flags & C3D_FRAME_SYNCDRAW)
170 if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1))
171 return false;
172
173 inFrame = true;
174 osTickCounterStart(&cpuTime);
175 GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
176 return true;
177}
178
179bool C3D_FrameDrawOn(C3D_RenderTarget* target)
180{
181 if (!inFrame) return false;
182
183 target->used = true;
184 C3D_SetFrameBuf(&target->frameBuf);
185 C3D_SetViewport(0, 0, target->frameBuf.width, target->frameBuf.height);
186 return true;
187}
188
189void C3D_FrameSplit(u8 flags)
190{
191 u32 *cmdBuf, cmdBufSize;
192 if (!inFrame) return;
193 if (C3Di_SplitFrame(&cmdBuf, &cmdBufSize))
194 GX_ProcessCommandList(cmdBuf, cmdBufSize*4, flags);
195}
196
197void C3D_FrameEnd(u8 flags)
198{
199 C3D_Context* ctx = C3Di_GetContext();
200 if (!inFrame) return;
201
202 if (frameEndCb)
203 frameEndCb(frameEndCbData);
204
205 C3D_FrameSplit(flags);
206 GPUCMD_SetBuffer(NULL, 0, 0);
207 osTickCounterUpdate(&cpuTime);
208 inFrame = false;
209
210 // Flush the entire linear memory if the user did not explicitly mandate to flush the command list
211 if (!(flags & GX_CMDLIST_FLUSH))
212 {
213 extern u32 __ctru_linear_heap;
214 extern u32 __ctru_linear_heap_size;
215 GSPGPU_FlushDataCache((void*)__ctru_linear_heap, __ctru_linear_heap_size);
216 }
217
218 int i;
219 C3D_RenderTarget* target;
220 isTopStereo = false;
221 for (i = 2; i >= 0; i --)
222 {
223 target = linkedTarget[i];
224 if (!target || !target->used)
225 continue;
226 target->used = false;
227 C3D_FrameBufTransfer(&target->frameBuf, target->screen, target->side, target->transferFlags);
228 if (target->screen == GFX_TOP)
229 {
230 needSwapTop = true;
231 if (target->side == GFX_RIGHT)
232 isTopStereo = true;
233 }
234 else if (target->screen == GFX_BOTTOM)
235 needSwapBot = true;
236 }
237
238 measureGpuTime = true;
239 osTickCounterStart(&gpuTime);
240 gxCmdQueueRun(&ctx->gxQueue);
241}
242
243void C3D_FrameEndHook(void (* hook)(void*), void* param)
244{
245 frameEndCb = hook;
246 frameEndCbData = param;
247}
248
250{
251 return osTickCounterRead(&gpuTime);
252}
253
255{
256 return osTickCounterRead(&cpuTime);
257}
258
259static C3D_RenderTarget* C3Di_RenderTargetNew(void)
260{
261 C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget));
262 if (!target) return NULL;
263 memset(target, 0, sizeof(C3D_RenderTarget));
264 return target;
265}
266
267static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target)
268{
269 target->prev = lastTarget;
270 target->next = NULL;
271 if (lastTarget)
272 lastTarget->next = target;
273 if (!firstTarget)
274 firstTarget = target;
275 lastTarget = target;
276}
277
278C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt)
279{
280 GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16;
281 void* depthBuf = NULL;
282 void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt));
283 if (!colorBuf) goto _fail0;
284 if (C3D_DEPTHTYPE_OK(depthFmt))
285 {
286 depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
287 size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmtReal);
288 vramAllocPos vramBank = addrGetVRAMBank(colorBuf);
289 depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
290 if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
291 if (!depthBuf) goto _fail1;
292 }
293
294 C3D_RenderTarget* target = C3Di_RenderTargetNew();
295 if (!target) goto _fail2;
296
297 C3D_FrameBuf* fb = &target->frameBuf;
298 C3D_FrameBufAttrib(fb, width, height, false);
299 C3D_FrameBufColor(fb, colorBuf, colorFmt);
300 target->ownsColor = true;
301 if (depthBuf)
302 {
303 C3D_FrameBufDepth(fb, depthBuf, depthFmtReal);
304 target->ownsDepth = true;
305 }
306 C3Di_RenderTargetFinishInit(target);
307 return target;
308
309_fail2:
310 if (depthBuf) vramFree(depthBuf);
311_fail1:
312 vramFree(colorBuf);
313_fail0:
314 return NULL;
315}
316
317C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt)
318{
319 if (!addrIsVRAM(tex->data)) return NULL; // Render targets must be in VRAM
320 C3D_RenderTarget* target = C3Di_RenderTargetNew();
321 if (!target) return NULL;
322
323 C3D_FrameBuf* fb = &target->frameBuf;
324 C3D_FrameBufTex(fb, tex, face, level);
325
326 if (C3D_DEPTHTYPE_OK(depthFmt))
327 {
328 GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
329 size_t depthSize = C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal);
330 vramAllocPos vramBank = addrGetVRAMBank(tex->data);
331 void* depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
332 if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
333 if (!depthBuf)
334 {
335 free(target);
336 return NULL;
337 }
338
339 C3D_FrameBufDepth(fb, depthBuf, depthFmtReal);
340 target->ownsDepth = true;
341 }
342
343 C3Di_RenderTargetFinishInit(target);
344 return target;
345}
346
347void C3Di_RenderTargetDestroy(C3D_RenderTarget* target)
348{
349 if (target->ownsColor)
350 vramFree(target->frameBuf.colorBuf);
351 if (target->ownsDepth)
352 vramFree(target->frameBuf.depthBuf);
353
354 C3D_RenderTarget** prevNext = target->prev ? &target->prev->next : &firstTarget;
355 C3D_RenderTarget** nextPrev = target->next ? &target->next->prev : &lastTarget;
356 *prevNext = target->next;
357 *nextPrev = target->prev;
358 free(target);
359}
360
361void C3D_RenderTargetDelete(C3D_RenderTarget* target)
362{
363 if (inFrame)
364 svcBreak(USERBREAK_PANIC); // Shouldn't happen.
365 if (target->linked)
366 C3D_RenderTargetDetachOutput(target);
367 else
368 C3Di_WaitAndClearQueue(-1);
369 C3Di_RenderTargetDestroy(target);
370}
371
372void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags)
373{
374 int id = 0;
375 if (screen==GFX_BOTTOM) id = 2;
376 else if (side==GFX_RIGHT) id = 1;
377 if (linkedTarget[id])
378 {
379 linkedTarget[id]->linked = false;
380 if (!inFrame)
381 C3Di_WaitAndClearQueue(-1);
382 }
383 linkedTarget[id] = target;
384 if (target)
385 {
386 target->linked = true;
387 target->transferFlags = transferFlags;
388 target->screen = screen;
389 target->side = side;
390 }
391}
392
393static void C3Di_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
394{
395 C3Di_WaitAndClearQueue(-1);
396 inSafeTransfer = true;
397 GX_DisplayTransfer(inadr, indim, outadr, outdim, flags);
398 gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
399}
400
401static void C3Di_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
402{
403 C3Di_WaitAndClearQueue(-1);
404 inSafeTransfer = true;
405 GX_TextureCopy(inadr, indim, outadr, outdim, size, flags);
406 gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
407}
408
409static void C3Di_SafeMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
410{
411 C3Di_WaitAndClearQueue(-1);
412 inSafeTransfer = true;
413 GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
414 gxCmdQueueRun(&C3Di_GetContext()->gxQueue);
415}
416
417void C3D_SyncDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)
418{
419 if (inFrame)
420 {
422 GX_DisplayTransfer(inadr, indim, outadr, outdim, flags);
423 } else
424 {
425 C3Di_SafeDisplayTransfer(inadr, indim, outadr, outdim, flags);
426 gspWaitForPPF();
427 }
428}
429
430void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags)
431{
432 if (inFrame)
433 {
435 GX_TextureCopy(inadr, indim, outadr, outdim, size, flags);
436 } else
437 {
438 C3Di_SafeTextureCopy(inadr, indim, outadr, outdim, size, flags);
439 gspWaitForPPF();
440 }
441}
442
443void C3D_SyncMemoryFill(u32* buf0a, u32 buf0v, u32* buf0e, u16 control0, u32* buf1a, u32 buf1v, u32* buf1e, u16 control1)
444{
445 if (inFrame)
446 {
448 GX_MemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
449 } else
450 {
451 C3Di_SafeMemoryFill(buf0a, buf0v, buf0e, control0, buf1a, buf1v, buf1e, control1);
452 gspWaitForPSC0();
453 }
454}
bool C3Di_SplitFrame(u32 **pBuf, u32 *pSize)
Definition: base.c:309
void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h)
Definition: base.c:136
void C3D_FrameBufTex(C3D_FrameBuf *fb, C3D_Tex *tex, GPU_TEXFACE face, int level)
Definition: framebuffer.c:41
void C3D_FrameBufTransfer(C3D_FrameBuf *frameBuf, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags)
Definition: framebuffer.c:95
u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt)
Definition: framebuffer.c:6
u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt)
Definition: framebuffer.c:12
void C3D_SetFrameBuf(C3D_FrameBuf *fb)
Definition: framebuffer.c:29
#define C3D_UNUSED
Definition: internal.h:10
void C3D_FrameEndHook(void(*hook)(void *), void *param)
Definition: renderqueue.c:243
void C3Di_RenderQueueInit(void)
Definition: renderqueue.c:114
bool C3D_FrameDrawOn(C3D_RenderTarget *target)
Definition: renderqueue.c:179
void C3D_FrameSplit(u8 flags)
Definition: renderqueue.c:189
C3D_RenderTarget * C3D_RenderTargetCreateFromTex(C3D_Tex *tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt)
Definition: renderqueue.c:317
void C3D_SyncTextureCopy(u32 *inadr, u32 indim, u32 *outadr, u32 outdim, u32 size, u32 flags)
Definition: renderqueue.c:430
void C3D_FrameSync(void)
Definition: renderqueue.c:75
float C3D_GetProcessingTime(void)
Definition: renderqueue.c:254
float C3D_GetDrawingTime(void)
Definition: renderqueue.c:249
u32 C3D_FrameCounter(int id)
Definition: renderqueue.c:87
C3D_RenderTarget * C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt)
Definition: renderqueue.c:278
void C3Di_RenderQueueDisableVBlank(void)
Definition: renderqueue.c:108
void C3Di_RenderQueueEnableVBlank(void)
Definition: renderqueue.c:102
bool C3D_FrameBegin(u8 flags)
Definition: renderqueue.c:163
void C3Di_RenderQueueWaitDone(void)
Definition: renderqueue.c:146
void C3D_SyncMemoryFill(u32 *buf0a, u32 buf0v, u32 *buf0e, u16 control0, u32 *buf1a, u32 buf1v, u32 *buf1e, u16 control1)
Definition: renderqueue.c:443
float C3D_FrameRate(float fps)
Definition: renderqueue.c:151
void C3D_RenderTargetSetOutput(C3D_RenderTarget *target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags)
Definition: renderqueue.c:372
void C3Di_RenderQueueExit(void)
Definition: renderqueue.c:125
void C3D_SyncDisplayTransfer(u32 *inadr, u32 indim, u32 *outadr, u32 outdim, u32 flags)
Definition: renderqueue.c:417
void C3D_RenderTargetDelete(C3D_RenderTarget *target)
Definition: renderqueue.c:361
void C3D_FrameEnd(u8 flags)
Definition: renderqueue.c:197
u32 * cmdBuf
Definition: internal.h:34
gxCmdQueue_s gxQueue
Definition: internal.h:33
size_t cmdBufSize
Definition: internal.h:35