Implement render queue system
This commit is contained in:
parent
f74c7b19ed
commit
39a71a5fbf
42
include/c3d/renderqueue.h
Normal file
42
include/c3d/renderqueue.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "renderbuffer.h"
|
||||||
|
|
||||||
|
typedef struct C3D_RenderTarget_tag C3D_RenderTarget;
|
||||||
|
|
||||||
|
struct C3D_RenderTarget_tag
|
||||||
|
{
|
||||||
|
C3D_RenderTarget *next, *prev, *link, *frame[2];
|
||||||
|
C3D_RenderBuf renderBuf;
|
||||||
|
u32 transferFlags;
|
||||||
|
|
||||||
|
u8 clearBits;
|
||||||
|
bool drawOk, transferOk;
|
||||||
|
|
||||||
|
bool linked;
|
||||||
|
gfxScreen_t screen;
|
||||||
|
gfx3dSide_t side;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags for C3D_FrameBegin
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
C3D_FRAME_SYNCDRAW = BIT(0), // Do not render the frame until the previous has finished rendering
|
||||||
|
C3D_FRAME_NONBLOCK = BIT(1), // Return false instead of waiting for the GPU to finish rendering
|
||||||
|
};
|
||||||
|
|
||||||
|
bool C3D_FrameBegin(u8 flags);
|
||||||
|
bool C3D_FrameDrawOn(C3D_RenderTarget* target);
|
||||||
|
void C3D_FrameEnd(u8 flags);
|
||||||
|
|
||||||
|
// Flags for C3D_RenderTargetSetClear (only C3D_CLEAR_ALL implemented atm)
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
C3D_CLEAR_COLOR = BIT(0),
|
||||||
|
C3D_CLEAR_DEPTH = BIT(1),
|
||||||
|
C3D_CLEAR_ALL = C3D_CLEAR_COLOR | C3D_CLEAR_DEPTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, int colorFmt, int depthFmt);
|
||||||
|
void C3D_RenderTargetDelete(C3D_RenderTarget* target);
|
||||||
|
void C3D_RenderTargetSetClear(C3D_RenderTarget* target, u32 clearBits, u32 clearColor, u32 clearDepth);
|
||||||
|
void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags);
|
@ -23,6 +23,7 @@ extern "C" {
|
|||||||
#include "c3d/light.h"
|
#include "c3d/light.h"
|
||||||
|
|
||||||
#include "c3d/renderbuffer.h"
|
#include "c3d/renderbuffer.h"
|
||||||
|
#include "c3d/renderqueue.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,14 @@ bool C3D_Init(size_t cmdBufSize)
|
|||||||
if (ctx->flags & C3DiF_Active)
|
if (ctx->flags & C3DiF_Active)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ctx->cmdBufSize = cmdBufSize;
|
ctx->cmdBufSize = cmdBufSize/8; // Half of the size of the cmdbuf, in words
|
||||||
ctx->cmdBuf = linearAlloc(cmdBufSize);
|
ctx->cmdBuf = (u32*)linearAlloc(cmdBufSize);
|
||||||
if (!ctx->cmdBuf) return false;
|
if (!ctx->cmdBuf) return false;
|
||||||
|
|
||||||
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
|
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
|
||||||
|
|
||||||
ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexAll;
|
ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexAll;
|
||||||
|
ctx->renderQueueExit = NULL;
|
||||||
|
|
||||||
// TODO: replace with direct struct access
|
// TODO: replace with direct struct access
|
||||||
C3D_DepthMap(-1.0f, 0.0f);
|
C3D_DepthMap(-1.0f, 0.0f);
|
||||||
@ -149,6 +150,7 @@ void C3Di_UpdateContext(void)
|
|||||||
{
|
{
|
||||||
ctx->flags &= ~C3DiF_DrawUsed;
|
ctx->flags &= ~C3DiF_DrawUsed;
|
||||||
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1);
|
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1);
|
||||||
|
GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1);
|
||||||
}
|
}
|
||||||
C3Di_RenderBufBind(ctx->rb);
|
C3Di_RenderBufBind(ctx->rb);
|
||||||
}
|
}
|
||||||
@ -251,13 +253,10 @@ void C3Di_UpdateContext(void)
|
|||||||
C3D_UpdateUniforms(GPU_GEOMETRY_SHADER);
|
C3D_UpdateUniforms(GPU_GEOMETRY_SHADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void C3D_FlushAsync(void)
|
void C3Di_FinalizeFrame(u32** pBuf, u32* pSize)
|
||||||
{
|
{
|
||||||
C3D_Context* ctx = C3Di_GetContext();
|
C3D_Context* ctx = C3Di_GetContext();
|
||||||
|
|
||||||
if (!(ctx->flags & C3DiF_Active))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ctx->flags & C3DiF_DrawUsed)
|
if (ctx->flags & C3DiF_DrawUsed)
|
||||||
{
|
{
|
||||||
ctx->flags &= ~C3DiF_DrawUsed;
|
ctx->flags &= ~C3DiF_DrawUsed;
|
||||||
@ -267,8 +266,30 @@ void C3D_FlushAsync(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GPUCMD_Finalize();
|
GPUCMD_Finalize();
|
||||||
GPUCMD_FlushAndRun();
|
GPUCMD_GetBuffer(pBuf, NULL, pSize);
|
||||||
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
|
*pSize *= 4;
|
||||||
|
|
||||||
|
ctx->flags ^= C3DiF_CmdBuffer;
|
||||||
|
u32* buf = ctx->cmdBuf;
|
||||||
|
if (ctx->flags & C3DiF_CmdBuffer)
|
||||||
|
buf += ctx->cmdBufSize;
|
||||||
|
GPUCMD_SetBuffer(buf, ctx->cmdBufSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void C3D_FlushAsync(void)
|
||||||
|
{
|
||||||
|
if (!(C3Di_GetContext()->flags & C3DiF_Active))
|
||||||
|
return;
|
||||||
|
|
||||||
|
u32* cmdBuf;
|
||||||
|
u32 cmdBufSize;
|
||||||
|
C3Di_FinalizeFrame(&cmdBuf, &cmdBufSize);
|
||||||
|
|
||||||
|
//take advantage of GX_FlushCacheRegions to flush gsp heap
|
||||||
|
extern u32 __ctru_linear_heap;
|
||||||
|
extern u32 __ctru_linear_heap_size;
|
||||||
|
GX_FlushCacheRegions(cmdBuf, cmdBufSize, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0);
|
||||||
|
GX_ProcessCommandList(cmdBuf, cmdBufSize, 0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void C3D_Fini(void)
|
void C3D_Fini(void)
|
||||||
@ -278,6 +299,9 @@ void C3D_Fini(void)
|
|||||||
if (!(ctx->flags & C3DiF_Active))
|
if (!(ctx->flags & C3DiF_Active))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (ctx->renderQueueExit)
|
||||||
|
ctx->renderQueueExit();
|
||||||
|
|
||||||
aptUnhook(&hookCookie);
|
aptUnhook(&hookCookie);
|
||||||
linearFree(ctx->cmdBuf);
|
linearFree(ctx->cmdBuf);
|
||||||
ctx->flags = 0;
|
ctx->flags = 0;
|
||||||
|
@ -26,7 +26,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
void* cmdBuf;
|
u32* cmdBuf;
|
||||||
size_t cmdBufSize;
|
size_t cmdBufSize;
|
||||||
|
|
||||||
u32 flags;
|
u32 flags;
|
||||||
@ -49,6 +49,8 @@ typedef struct
|
|||||||
u16 fixedAttribDirty, fixedAttribEverDirty;
|
u16 fixedAttribDirty, fixedAttribEverDirty;
|
||||||
C3D_FVec fixedAttribs[12];
|
C3D_FVec fixedAttribs[12];
|
||||||
|
|
||||||
|
void (* renderQueueExit)(void);
|
||||||
|
|
||||||
} C3D_Context;
|
} C3D_Context;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -66,6 +68,7 @@ enum
|
|||||||
C3DiF_LightEnv = BIT(10),
|
C3DiF_LightEnv = BIT(10),
|
||||||
C3DiF_VshCode = BIT(11),
|
C3DiF_VshCode = BIT(11),
|
||||||
C3DiF_GshCode = BIT(12),
|
C3DiF_GshCode = BIT(12),
|
||||||
|
C3DiF_CmdBuffer = BIT(13),
|
||||||
|
|
||||||
#define C3DiF_Tex(n) BIT(23+(n))
|
#define C3DiF_Tex(n) BIT(23+(n))
|
||||||
C3DiF_TexAll = 7 << 23,
|
C3DiF_TexAll = 7 << 23,
|
||||||
@ -91,3 +94,5 @@ void C3Di_LightMtlBlend(C3D_Light* light);
|
|||||||
void C3Di_DirtyUniforms(GPU_SHADER_TYPE type);
|
void C3Di_DirtyUniforms(GPU_SHADER_TYPE type);
|
||||||
void C3Di_LoadShaderUniforms(shaderInstance_s* si);
|
void C3Di_LoadShaderUniforms(shaderInstance_s* si);
|
||||||
void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type);
|
void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type);
|
||||||
|
|
||||||
|
void C3Di_FinalizeFrame(u32** pBuf, u32* pSize);
|
||||||
|
354
source/renderqueue.c
Normal file
354
source/renderqueue.c
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
#include "context.h"
|
||||||
|
#include <c3d/renderqueue.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static C3D_RenderTarget *firstTarget, *lastTarget;
|
||||||
|
static C3D_RenderTarget *linkedTarget[3];
|
||||||
|
static C3D_RenderTarget *transferQueue, *clearQueue;
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* targetList;
|
||||||
|
u32* cmdBuf;
|
||||||
|
u32 cmdBufSize;
|
||||||
|
u8 flags;
|
||||||
|
} queuedFrame[2];
|
||||||
|
static u8 queueSwap, queuedCount, queuedState;
|
||||||
|
|
||||||
|
static bool inFrame;
|
||||||
|
|
||||||
|
static void onRenderFinish(void* unused);
|
||||||
|
static void onTransferFinish(void* unused);
|
||||||
|
static void onClearDone(void* unused);
|
||||||
|
|
||||||
|
static void performDraw(void)
|
||||||
|
{
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_P3D, onRenderFinish, NULL, true);
|
||||||
|
GX_ProcessCommandList(queuedFrame[queueSwap].cmdBuf, queuedFrame[queueSwap].cmdBufSize, queuedFrame[queueSwap].flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void performTransfer(void)
|
||||||
|
{
|
||||||
|
C3D_RenderBuf* renderBuf = &transferQueue->renderBuf;
|
||||||
|
u32* frameBuf = (u32*)gfxGetFramebuffer(transferQueue->screen, transferQueue->side, NULL, NULL);
|
||||||
|
if (transferQueue->side == GFX_LEFT)
|
||||||
|
gfxConfigScreen(transferQueue->screen, false);
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_PPF, onTransferFinish, NULL, true);
|
||||||
|
C3D_RenderBufTransferAsync(renderBuf, frameBuf, transferQueue->transferFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void performClear(void)
|
||||||
|
{
|
||||||
|
C3D_RenderBuf* renderBuf = &clearQueue->renderBuf;
|
||||||
|
// TODO: obey renderBuf->clearBits
|
||||||
|
gspSetEventCallback(renderBuf->colorBuf.data ? GSPGPU_EVENT_PSC0 : GSPGPU_EVENT_PSC1, onClearDone, NULL, true);
|
||||||
|
C3D_RenderBufClearAsync(renderBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateFrameQueue(void)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* a;
|
||||||
|
if (queuedState>0) return; // Still rendering
|
||||||
|
|
||||||
|
// Check that all targets are OK to be drawn on
|
||||||
|
for (a = queuedFrame[queueSwap].targetList; a; a = a->frame[queueSwap])
|
||||||
|
if (!a->drawOk)
|
||||||
|
return; // Nope, we can't start rendering yet
|
||||||
|
|
||||||
|
// Start rendering the frame
|
||||||
|
queuedState=1;
|
||||||
|
for (a = queuedFrame[queueSwap].targetList; a; a = a->frame[queueSwap])
|
||||||
|
a->drawOk = false;
|
||||||
|
performDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transferTarget(C3D_RenderTarget* target)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* a;
|
||||||
|
target->transferOk = false;
|
||||||
|
target->link = NULL;
|
||||||
|
if (!transferQueue)
|
||||||
|
{
|
||||||
|
transferQueue = target;
|
||||||
|
performTransfer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (a = transferQueue; a->link; a = a->link);
|
||||||
|
a->link = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearTarget(C3D_RenderTarget* target)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* a;
|
||||||
|
target->link = NULL;
|
||||||
|
if (!clearQueue)
|
||||||
|
{
|
||||||
|
clearQueue = target;
|
||||||
|
performClear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (a = clearQueue; a->link; a = a->link);
|
||||||
|
a->link = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onVBlank0(void* unused)
|
||||||
|
{
|
||||||
|
if (!linkedTarget[0]) return;
|
||||||
|
|
||||||
|
if (gfxIs3D())
|
||||||
|
{
|
||||||
|
if (linkedTarget[1] && linkedTarget[1]->transferOk)
|
||||||
|
transferTarget(linkedTarget[1]);
|
||||||
|
else if (linkedTarget[0]->transferOk)
|
||||||
|
{
|
||||||
|
// Use a temporary copy of the left framebuffer to fill in the missing right image.
|
||||||
|
static C3D_RenderTarget temp;
|
||||||
|
memcpy(&temp, linkedTarget[0], sizeof(temp));
|
||||||
|
temp.side = GFX_RIGHT;
|
||||||
|
temp.clearBits = false;
|
||||||
|
transferTarget(&temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (linkedTarget[0]->transferOk)
|
||||||
|
transferTarget(linkedTarget[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onVBlank1(void* unused)
|
||||||
|
{
|
||||||
|
if (linkedTarget[2] && linkedTarget[2]->transferOk)
|
||||||
|
transferTarget(linkedTarget[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRenderFinish(void* unused)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget *a, *next;
|
||||||
|
|
||||||
|
// The following check should never trigger
|
||||||
|
if (queuedState!=1) svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
|
for (a = queuedFrame[queueSwap].targetList; a; a = next)
|
||||||
|
{
|
||||||
|
next = a->frame[queueSwap];
|
||||||
|
a->frame[queueSwap] = NULL;
|
||||||
|
if (a->linked)
|
||||||
|
a->transferOk = true;
|
||||||
|
else if (a->clearBits)
|
||||||
|
clearTarget(a);
|
||||||
|
else
|
||||||
|
a->drawOk = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume the frame that has been just rendered
|
||||||
|
memset(&queuedFrame[queueSwap], 0, sizeof(queuedFrame[queueSwap]));
|
||||||
|
queueSwap ^= 1;
|
||||||
|
queuedCount--;
|
||||||
|
queuedState = 0;
|
||||||
|
|
||||||
|
// Update the frame queue if there are still frames to render
|
||||||
|
if (queuedCount>0)
|
||||||
|
updateFrameQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTransferFinish(void* unused)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* target = transferQueue;
|
||||||
|
transferQueue = target->link;
|
||||||
|
if (target->clearBits)
|
||||||
|
clearTarget(target);
|
||||||
|
else
|
||||||
|
target->drawOk = true;
|
||||||
|
if (transferQueue)
|
||||||
|
performTransfer();
|
||||||
|
if (target->drawOk && queuedCount>0 && queuedState==0)
|
||||||
|
updateFrameQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onClearDone(void* unused)
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* target = clearQueue;
|
||||||
|
clearQueue = target->link;
|
||||||
|
target->drawOk = true;
|
||||||
|
if (clearQueue)
|
||||||
|
performClear();
|
||||||
|
if (queuedCount>0 && queuedState==0)
|
||||||
|
updateFrameQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void C3Di_RenderQueueInit(void)
|
||||||
|
{
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void C3Di_RenderQueueExit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
C3D_RenderTarget *a, *next;
|
||||||
|
|
||||||
|
if (inFrame) return;
|
||||||
|
|
||||||
|
for (a = firstTarget; a; a = next)
|
||||||
|
{
|
||||||
|
next = a->next;
|
||||||
|
C3D_RenderTargetDelete(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
|
||||||
|
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i ++)
|
||||||
|
linkedTarget[i] = NULL;
|
||||||
|
|
||||||
|
memset(queuedFrame, 0, sizeof(queuedFrame));
|
||||||
|
queueSwap = 0;
|
||||||
|
queuedCount = 0;
|
||||||
|
queuedState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkRenderQueueInit(void)
|
||||||
|
{
|
||||||
|
C3D_Context* ctx = C3Di_GetContext();
|
||||||
|
|
||||||
|
if (!(ctx->flags & C3DiF_Active))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ctx->renderQueueExit)
|
||||||
|
{
|
||||||
|
C3Di_RenderQueueInit();
|
||||||
|
ctx->renderQueueExit = C3Di_RenderQueueExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool C3D_FrameBegin(u8 flags)
|
||||||
|
{
|
||||||
|
if (inFrame) return false;
|
||||||
|
int maxCount = (flags & C3D_FRAME_SYNCDRAW) ? 1 : 2;
|
||||||
|
while (queuedCount >= maxCount)
|
||||||
|
{
|
||||||
|
if (flags & C3D_FRAME_NONBLOCK)
|
||||||
|
return false;
|
||||||
|
gspWaitForP3D();
|
||||||
|
}
|
||||||
|
inFrame = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool C3D_FrameDrawOn(C3D_RenderTarget* target)
|
||||||
|
{
|
||||||
|
if (!inFrame) return false;
|
||||||
|
|
||||||
|
// Queue the target in the frame if it hasn't already been.
|
||||||
|
int pos = queueSwap^queuedCount;
|
||||||
|
if (!target->frame[pos])
|
||||||
|
{
|
||||||
|
if (!queuedFrame[pos].targetList)
|
||||||
|
queuedFrame[pos].targetList = target;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
C3D_RenderTarget* a;
|
||||||
|
for (a = queuedFrame[pos].targetList; a->frame[pos]; a = a->frame[pos]);
|
||||||
|
a->frame[pos] = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C3D_RenderBufBind(&target->renderBuf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C3D_FrameEnd(u8 flags)
|
||||||
|
{
|
||||||
|
if (!inFrame) return;
|
||||||
|
inFrame = false;
|
||||||
|
|
||||||
|
int pos = queueSwap^queuedCount;
|
||||||
|
if (!queuedFrame[pos].targetList) return;
|
||||||
|
|
||||||
|
// Add the frame to the queue
|
||||||
|
queuedCount++;
|
||||||
|
C3Di_FinalizeFrame(&queuedFrame[pos].cmdBuf, &queuedFrame[pos].cmdBufSize);
|
||||||
|
queuedFrame[pos].flags = flags;
|
||||||
|
|
||||||
|
// Flush the entire linear memory if the user did not explicitly mandate to flush the command list
|
||||||
|
if (!(flags & GX_CMDLIST_FLUSH))
|
||||||
|
{
|
||||||
|
// Take advantage of GX_FlushCacheRegions to flush gsp heap
|
||||||
|
extern u32 __ctru_linear_heap;
|
||||||
|
extern u32 __ctru_linear_heap_size;
|
||||||
|
GX_FlushCacheRegions(queuedFrame[queueSwap].cmdBuf, queuedFrame[queueSwap].cmdBufSize, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the frame queue
|
||||||
|
updateFrameQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, int colorFmt, int depthFmt)
|
||||||
|
{
|
||||||
|
if (!checkRenderQueueInit()) return NULL;
|
||||||
|
C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget));
|
||||||
|
if (!target) return NULL;
|
||||||
|
memset(target, 0, sizeof(C3D_RenderTarget));
|
||||||
|
if (!C3D_RenderBufInit(&target->renderBuf, width, height, colorFmt, depthFmt))
|
||||||
|
{
|
||||||
|
free(target);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->drawOk = true;
|
||||||
|
target->prev = lastTarget;
|
||||||
|
target->next = NULL;
|
||||||
|
if (lastTarget)
|
||||||
|
lastTarget->next = target;
|
||||||
|
if (!firstTarget)
|
||||||
|
firstTarget = target;
|
||||||
|
lastTarget = target;
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C3D_RenderTargetDelete(C3D_RenderTarget* target)
|
||||||
|
{
|
||||||
|
while (!target->drawOk)
|
||||||
|
gspWaitForAnyEvent();
|
||||||
|
C3D_RenderBufDelete(&target->renderBuf);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void C3D_RenderTargetSetClear(C3D_RenderTarget* target, u32 clearBits, u32 clearColor, u32 clearDepth)
|
||||||
|
{
|
||||||
|
if (!target->drawOk) return;
|
||||||
|
|
||||||
|
if (target->renderBuf.colorBuf.data==NULL) clearBits &= ~C3D_CLEAR_COLOR;
|
||||||
|
if (target->renderBuf.depthBuf.data==NULL) clearBits &= ~C3D_CLEAR_DEPTH;
|
||||||
|
|
||||||
|
u32 oldClearBits = target->clearBits;
|
||||||
|
target->clearBits = clearBits & 0xFF;
|
||||||
|
target->renderBuf.clearColor = clearColor;
|
||||||
|
target->renderBuf.clearDepth = clearDepth;
|
||||||
|
|
||||||
|
if (clearBits &~ oldClearBits)
|
||||||
|
{
|
||||||
|
target->drawOk = false;
|
||||||
|
clearTarget(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
linkedTarget[id]->linked = false;
|
||||||
|
linkedTarget[id] = target;
|
||||||
|
target->linked = true;
|
||||||
|
target->transferFlags = transferFlags;
|
||||||
|
target->screen = screen;
|
||||||
|
target->side = side;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user