From efa38fe4f40b59491d91d2a2ddd4489cea050ab0 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 7 Apr 2024 09:06:34 +1000 Subject: [PATCH] 3DS: Embed citro3d source directly into the graphics backend --- misc/3ds/Makefile | 4 +- src/Graphics_3DS.c | 18 +- third_party/citro3d.c | 2012 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2023 insertions(+), 11 deletions(-) create mode 100644 third_party/citro3d.c diff --git a/misc/3ds/Makefile b/misc/3ds/Makefile index 02fd2a28e..f0b91f031 100644 --- a/misc/3ds/Makefile +++ b/misc/3ds/Makefile @@ -48,7 +48,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lcitro3d -lctru -lm +LIBS := -lctru -lm #--------------------------------------------------------------------------------- @@ -133,4 +133,4 @@ $(OUTPUT).elf : $(OFILES) #--------------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------------- diff --git a/src/Graphics_3DS.c b/src/Graphics_3DS.c index 0f1f6f56d..a2f78464a 100644 --- a/src/Graphics_3DS.c +++ b/src/Graphics_3DS.c @@ -5,8 +5,8 @@ #include "Logger.h" #include "Window.h" #include <3ds.h> -#include #define BUFFER_BASE_PADDR OS_VRAM_PADDR // VRAM physical address +#include "../third_party/citro3d.c" // autogenerated from the .v.pica files by Makefile extern const u8 coloured_shbin[]; @@ -204,7 +204,6 @@ static void Calc3DProjection(int dir, struct Matrix* proj) { float slider = osGet3DSliderState(); float iod = (slider / 3) * dir; float shift = iod / (2.0f * 2.0f); - Platform_Log2("ADJ: %f3, %f3", &shift, &proj->row1.y); proj_adj.row3.y = 1.0f * shift * -proj->row1.y; Gfx.Projection = proj_adj; @@ -596,19 +595,20 @@ static void GPUBuffers_DeleteUnreferenced(void) { static cc_uint16* gfx_indices; GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { - struct GPUBuffer* buffer = GPUBuffer_Alloc(count, sizeof(cc_uint16)); - if (!buffer) Logger_Abort("Failed to allocate memory for index buffer"); + if (!gfx_indices) { + gfx_indices = linearAlloc(count * sizeof(cc_uint16)); + if (!gfx_indices) Logger_Abort("Failed to allocate memory for index buffer"); + } - fillFunc((cc_uint16*)buffer->data, count, obj); - return buffer; + fillFunc(gfx_indices, count, obj); + return gfx_indices; } void Gfx_BindIb(GfxResourceID ib) { - struct GPUBuffer* buffer = (struct GPUBuffer*)ib; - gfx_indices = (cc_uint16*)buffer->data; + // No need to bind again } -void Gfx_DeleteIb(GfxResourceID* ib) { GPUBuffer_Unref(ib); } +void Gfx_DeleteIb(GfxResourceID* ib) { } /*########################################################################################################################* diff --git a/third_party/citro3d.c b/third_party/citro3d.c new file mode 100644 index 000000000..3e474a553 --- /dev/null +++ b/third_party/citro3d.c @@ -0,0 +1,2012 @@ +/* +Copyright (C) 2014-2018 fincs + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source + distribution. +*/ +#include +#include +#include +#include +#include + +typedef u32 C3D_IVec; + +typedef union +{ + struct + { + float w; ///< W-component + float z; ///< Z-component + float y; ///< Y-component + float x; ///< X-component + }; + float c[4]; +} C3D_FVec; + +typedef union +{ + C3D_FVec r[4]; ///< Rows are vectors + float m[4*4]; ///< Raw access +} C3D_Mtx; + + +typedef struct +{ + u32 flags[2]; + u64 permutation; + int attrCount; +} C3D_AttrInfo; + +static void AttrInfo_Init(C3D_AttrInfo* info); +static int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count); + +static C3D_AttrInfo* C3D_GetAttrInfo(void); + + + + +typedef struct +{ + u32 offset; + u32 flags[2]; +} C3D_BufCfg; + +typedef struct +{ + int bufCount; + C3D_BufCfg buffers[12]; +} C3D_BufInfo; + +static void BufInfo_Init(C3D_BufInfo* info); +static int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation); + +static C3D_BufInfo* C3D_GetBufInfo(void); + + + + + + + +typedef struct +{ + void* data; + GPU_TEXCOLOR fmt : 4; + size_t size : 28; + + union + { + u32 dim; + struct + { + u16 height; + u16 width; + }; + }; + + u32 param; + u32 border; + union + { + u32 lodParam; + struct + { + u16 lodBias; + u8 maxLevel; + u8 minLevel; + }; + }; +} C3D_Tex; + +typedef struct CTR_ALIGN(8) +{ + u16 width; + u16 height; + u8 maxLevel : 4; + GPU_TEXCOLOR format : 4; + bool onVram : 1; +} C3D_TexInitParams; + +static bool C3D_TexInitWithParams(C3D_Tex* tex, void* cube, C3D_TexInitParams p); +static void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level); +static void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face); +static void C3D_TexBind(int unitId, C3D_Tex* tex); +static void C3D_TexFlush(C3D_Tex* tex); +static void C3D_TexDelete(C3D_Tex* tex); + +static inline int C3D_TexCalcMaxLevel(u32 width, u32 height) +{ + return (31-__builtin_clz(width < height ? width : height)) - 3; // avoid sizes smaller than 8 +} + +static inline u32 C3D_TexCalcLevelSize(u32 size, int level) +{ + return size >> (2*level); +} + +static inline u32 C3D_TexCalcTotalSize(u32 size, int maxLevel) +{ + /* + S = s + sr + sr^2 + sr^3 + ... + sr^n + Sr = sr + sr^2 + sr^3 + ... + sr^(n+1) + S-Sr = s - sr^(n+1) + S(1-r) = s(1 - r^(n+1)) + S = s (1 - r^(n+1)) / (1-r) + + r = 1/4 + 1-r = 3/4 + + S = 4s (1 - (1/4)^(n+1)) / 3 + S = 4s (1 - 1/4^(n+1)) / 3 + S = (4/3) (s - s/4^(n+1)) + S = (4/3) (s - s/(1<<(2n+2))) + S = (4/3) (s - s>>(2n+2)) + */ + return (size - C3D_TexCalcLevelSize(size,maxLevel+1)) * 4 / 3; +} + +static inline bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) +{ + return C3D_TexInitWithParams(tex, NULL, + (C3D_TexInitParams){ width, height, 0, format, false }); +} + +static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) +{ + return C3D_TexInitWithParams(tex, NULL, + (C3D_TexInitParams){ width, height, 0, format, true }); +} + +static inline GPU_TEXTURE_MODE_PARAM C3D_TexGetType(C3D_Tex* tex) +{ + return (GPU_TEXTURE_MODE_PARAM)((tex->param>>28)&0x7); +} + +static inline void* C3D_TexGetImagePtr(C3D_Tex* tex, void* data, int level, u32* size) +{ + if (size) *size = level >= 0 ? C3D_TexCalcLevelSize(tex->size, level) : C3D_TexCalcTotalSize(tex->size, tex->maxLevel); + if (!level) return data; + return (u8*)data + (level > 0 ? C3D_TexCalcTotalSize(tex->size, level-1) : 0); +} + +static inline void* C3D_Tex2DGetImagePtr(C3D_Tex* tex, int level, u32* size) +{ + return C3D_TexGetImagePtr(tex, tex->data, level, size); +} + +static inline void C3D_TexUpload(C3D_Tex* tex, const void* data) +{ + C3D_TexLoadImage(tex, data, GPU_TEXFACE_2D, 0); +} + +static inline void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) +{ + tex->param &= ~(GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)); + tex->param |= GPU_TEXTURE_MAG_FILTER(magFilter) | GPU_TEXTURE_MIN_FILTER(minFilter); +} + +static inline void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT) +{ + tex->param &= ~(GPU_TEXTURE_WRAP_S(3) | GPU_TEXTURE_WRAP_T(3)); + tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT); +} + + + + + + + + + + +static void C3D_DepthMap(bool bIsZBuffer, float zScale, float zOffset); +static void C3D_CullFace(GPU_CULLMODE mode); +static void C3D_StencilTest(void); +static void C3D_StencilOp(void); +static void C3D_EarlyDepthTest(bool enable, GPU_EARLYDEPTHFUNC function, u32 ref); +static void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask); +static void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref); +static void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha); +static void C3D_ColorLogicOp(GPU_LOGICOP op); +static void C3D_FragOpMode(GPU_FRAGOPMODE mode); +static void C3D_FragOpShadow(float scale, float bias); + + + + + + +#define C3D_DEFAULT_CMDBUF_SIZE 0x40000 + +enum +{ + C3D_UNSIGNED_BYTE = 0, + C3D_UNSIGNED_SHORT = 1, +}; + +static bool C3D_Init(size_t cmdBufSize); +static void C3D_Fini(void); + +static void C3D_BindProgram(shaderProgram_s* program); + +static void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h); +static void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom); + +static void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices); + +// Immediate-mode vertex submission +static void C3D_ImmDrawBegin(GPU_Primitive_t primitive); +static void C3D_ImmSendAttrib(float x, float y, float z, float w); +static void C3D_ImmDrawEnd(void); + +static inline void C3D_ImmDrawRestartPrim(void) +{ + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); +} + + + + + +typedef struct +{ + u32 data[128]; +} C3D_FogLut; + +static inline float FogLut_CalcZ(float depth, float near, float far) +{ + return far*near/(depth*(far-near)+near); +} + +static void FogLut_FromArray(C3D_FogLut* lut, const float data[256]); +static void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, float far); + +static void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip); +static void C3D_FogColor(u32 color); +static void C3D_FogLutBind(C3D_FogLut* lut); + + + + + + + + + + + +typedef struct +{ + void* colorBuf; + void* depthBuf; + u16 width; + u16 height; + GPU_COLORBUF colorFmt; + GPU_DEPTHBUF depthFmt; + bool block32; + u8 colorMask : 4; + u8 depthMask : 4; +} C3D_FrameBuf; + +// Flags for C3D_FrameBufClear +typedef enum +{ + C3D_CLEAR_COLOR = BIT(0), + C3D_CLEAR_DEPTH = BIT(1), + C3D_CLEAR_ALL = C3D_CLEAR_COLOR | C3D_CLEAR_DEPTH, +} C3D_ClearBits; + +static u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt); +static u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt); + +static C3D_FrameBuf* C3D_GetFrameBuf(void); +static void C3D_SetFrameBuf(C3D_FrameBuf* fb); +static void C3D_FrameBufClear(C3D_FrameBuf* fb, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth); +static void C3D_FrameBufTransfer(C3D_FrameBuf* fb, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); + +static inline void C3D_FrameBufAttrib(C3D_FrameBuf* fb, u16 width, u16 height, bool block32) +{ + fb->width = width; + fb->height = height; + fb->block32 = block32; +} + +static inline void C3D_FrameBufColor(C3D_FrameBuf* fb, void* buf, GPU_COLORBUF fmt) +{ + if (buf) + { + fb->colorBuf = buf; + fb->colorFmt = fmt; + fb->colorMask = 0xF; + } else + { + fb->colorBuf = NULL; + fb->colorFmt = GPU_RB_RGBA8; + fb->colorMask = 0; + } +} + +static inline void C3D_FrameBufDepth(C3D_FrameBuf* fb, void* buf, GPU_DEPTHBUF fmt) +{ + if (buf) + { + fb->depthBuf = buf; + fb->depthFmt = fmt; + fb->depthMask = fmt == GPU_RB_DEPTH24_STENCIL8 ? 0x3 : 0x2; + } else + { + fb->depthBuf = NULL; + fb->depthFmt = GPU_RB_DEPTH24; + fb->depthMask = 0; + } +} + + + + + + + +typedef struct C3D_RenderTarget_tag C3D_RenderTarget; + +struct C3D_RenderTarget_tag +{ + C3D_RenderTarget *next, *prev; + C3D_FrameBuf frameBuf; + + bool used; + bool ownsColor, ownsDepth; + + bool linked; + gfxScreen_t screen; + gfx3dSide_t side; + u32 transferFlags; +}; + +// 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); +static void C3D_FrameEnd(u8 flags); + +static C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, GPU_DEPTHBUF depthFmt); +static void C3D_RenderTargetDelete(C3D_RenderTarget* target); +static void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags); + +static inline void C3D_RenderTargetDetachOutput(C3D_RenderTarget* target) +{ + C3D_RenderTargetSetOutput(NULL, target->screen, target->side, 0); +} + +static inline void C3D_RenderTargetClear(C3D_RenderTarget* target, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth) +{ + C3D_FrameBufClear(&target->frameBuf, clearBits, clearColor, clearDepth); +} + +static void C3D_SyncTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags); + + + + +typedef struct +{ + u16 srcRgb, srcAlpha; + union + { + u32 opAll; + struct { u32 opRgb:12, opAlpha:12; }; + }; + u16 funcRgb, funcAlpha; + u32 color; + u16 scaleRgb, scaleAlpha; +} C3D_TexEnv; + +static inline void C3D_TexEnvInit(C3D_TexEnv* env) +{ + env->srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0); + env->srcAlpha = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0); + env->opAll = 0; + env->funcRgb = GPU_REPLACE; + env->funcAlpha = GPU_REPLACE; + env->color = 0xFFFFFFFF; + env->scaleRgb = GPU_TEVSCALE_1; + env->scaleAlpha = GPU_TEVSCALE_1; +} + + +void Mtx_Multiply(C3D_Mtx* out, const C3D_Mtx* a, const C3D_Mtx* b) +{ + // http://www.wolframalpha.com/input/?i={{a,b,c,d},{e,f,g,h},{i,j,k,l},{m,n,o,p}}{{α,β,γ,δ},{ε,θ,ι,κ},{λ,μ,ν,ξ},{ο,π,ρ,σ}} + int i, j; + for (j = 0; j < 4; ++j) + for (i = 0; i < 4; ++i) + out->r[j].c[i] = a->r[j].x*b->r[0].c[i] + a->r[j].y*b->r[1].c[i] + a->r[j].z*b->r[2].c[i] + a->r[j].w*b->r[3].c[i]; +} + + + + +#define C3D_FVUNIF_COUNT 96 +#define C3D_IVUNIF_COUNT 4 + +static C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; +static C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; +static u16 C3D_BoolUnifs[2]; + +static bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; +static bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; +static bool C3D_BoolUnifsDirty[2]; + +static inline C3D_FVec* C3D_FVUnifWritePtr(GPU_SHADER_TYPE type, int id, int size) +{ + int i; + for (i = 0; i < size; i ++) + C3D_FVUnifDirty[type][id+i] = true; + return &C3D_FVUnif[type][id]; +} + +static inline void C3D_FVUnifMtxNx4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx, int num) +{ + int i; + C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, num); + for (i = 0; i < num; i ++) + ptr[i] = mtx->r[i]; // Struct copy. +} + +static inline void C3D_FVUnifMtx4x4(GPU_SHADER_TYPE type, int id, const C3D_Mtx* mtx) +{ + C3D_FVUnifMtxNx4(type, id, mtx, 4); +} + +static inline void C3D_FVUnifSet(GPU_SHADER_TYPE type, int id, float x, float y, float z, float w) +{ + C3D_FVec* ptr = C3D_FVUnifWritePtr(type, id, 1); + ptr->x = x; + ptr->y = y; + ptr->z = z; + ptr->w = w; +} + +static void C3D_UpdateUniforms(GPU_SHADER_TYPE type); + + + + + + +typedef struct +{ + u32 fragOpMode; + u32 fragOpShadow; + u32 zScale, zOffset; + GPU_CULLMODE cullMode; + bool zBuffer, earlyDepth; + GPU_EARLYDEPTHFUNC earlyDepthFunc; + u32 earlyDepthRef; + + u32 alphaTest; + u32 stencilMode, stencilOp; + u32 depthTest; + + u32 alphaBlend; + GPU_LOGICOP clrLogicOp; +} C3D_Effect; + +typedef struct +{ + gxCmdQueue_s gxQueue; + u32* cmdBuf; + size_t cmdBufSize; + + u32 flags; + shaderProgram_s* program; + + C3D_AttrInfo attrInfo; + C3D_BufInfo bufInfo; + C3D_Effect effect; + + u32 texConfig; + u32 texShadow; + C3D_Tex* tex[3]; + C3D_TexEnv texEnv[6]; + + u32 texEnvBuf, texEnvBufClr; + u32 fogClr; + C3D_FogLut* fogLut; + + C3D_FrameBuf fb; + u32 viewport[5]; + u32 scissor[3]; +} C3D_Context; + +enum +{ + C3DiF_Active = BIT(0), + C3DiF_DrawUsed = BIT(1), + C3DiF_AttrInfo = BIT(2), + C3DiF_BufInfo = BIT(3), + C3DiF_Effect = BIT(4), + C3DiF_FrameBuf = BIT(5), + C3DiF_Viewport = BIT(6), + C3DiF_Scissor = BIT(7), + C3DiF_Program = BIT(8), + C3DiF_TexEnvBuf = BIT(9), + C3DiF_LightEnv = BIT(10), + C3DiF_VshCode = BIT(11), + C3DiF_GshCode = BIT(12), + C3DiF_TexStatus = BIT(14), + C3DiF_FogLut = BIT(17), + C3DiF_Gas = BIT(18), + +#define C3DiF_Tex(n) BIT(23+(n)) + C3DiF_TexAll = 7 << 23, +#define C3DiF_TexEnv(n) BIT(26+(n)) + C3DiF_TexEnvAll = 0x3F << 26, +}; + +static C3D_Context __C3D_Context; +static inline C3D_Context* C3Di_GetContext(void) +{ + extern C3D_Context __C3D_Context; + return &__C3D_Context; +} + +static inline bool addrIsVRAM(const void* addr) +{ + u32 vaddr = (u32)addr; + return vaddr >= OS_VRAM_VADDR && vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE; +} + +static inline vramAllocPos addrGetVRAMBank(const void* addr) +{ + u32 vaddr = (u32)addr; + return vaddr < OS_VRAM_VADDR + OS_VRAM_SIZE/2 ? VRAM_ALLOC_A : VRAM_ALLOC_B; +} + +static void C3Di_UpdateContext(void); +static void C3Di_AttrInfoBind(C3D_AttrInfo* info); +static void C3Di_BufInfoBind(C3D_BufInfo* info); +static void C3Di_FrameBufBind(C3D_FrameBuf* fb); +static void C3Di_TexEnvBind(int id, C3D_TexEnv* env); +static void C3Di_SetTex(int unit, C3D_Tex* tex); +static void C3Di_EffectBind(C3D_Effect* effect); + +static void C3Di_DirtyUniforms(GPU_SHADER_TYPE type); +static void C3Di_LoadShaderUniforms(shaderInstance_s* si); +static void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type); + +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); + + + + + + + + + +static void AttrInfo_Init(C3D_AttrInfo* info) +{ + memset(info, 0, sizeof(*info)); + info->flags[1] = 0xFFF << 16; +} + +static int AttrInfo_AddLoader(C3D_AttrInfo* info, int regId, GPU_FORMATS format, int count) +{ + if (info->attrCount == 12) return -1; + int id = info->attrCount++; + if (regId < 0) regId = id; + if (id < 8) + info->flags[0] |= GPU_ATTRIBFMT(id, count, format); + else + info->flags[1] |= GPU_ATTRIBFMT(id-8, count, format); + + info->flags[1] = (info->flags[1] &~ (0xF0000000 | BIT(id+16))) | (id << 28); + info->permutation |= regId << (id*4); + return id; +} + +static C3D_AttrInfo* C3D_GetAttrInfo(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + ctx->flags |= C3DiF_AttrInfo; + return &ctx->attrInfo; +} + +static void C3Di_AttrInfoBind(C3D_AttrInfo* info) +{ + GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFERS_FORMAT_LOW, (u32*)info->flags, sizeof(info->flags)/sizeof(u32)); + GPUCMD_AddMaskedWrite(GPUREG_VSH_INPUTBUFFER_CONFIG, 0xB, 0xA0000000 | (info->attrCount - 1)); + GPUCMD_AddWrite(GPUREG_VSH_NUM_ATTR, info->attrCount - 1); + GPUCMD_AddIncrementalWrites(GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW, (u32*)&info->permutation, 2); +} + + + + + + + + + + + +#define BUFFER_BASE_PADDR 0x18000000 + +static void BufInfo_Init(C3D_BufInfo* info) +{ + memset(info, 0, sizeof(*info)); +} + +static int BufInfo_Add(C3D_BufInfo* info, const void* data, ptrdiff_t stride, int attribCount, u64 permutation) +{ + if (info->bufCount == 12) return -1; + int id = info->bufCount++; + + u32 pa = osConvertVirtToPhys(data); + if (pa < BUFFER_BASE_PADDR) return -2; + + C3D_BufCfg* buf = &info->buffers[id]; + buf->offset = pa - BUFFER_BASE_PADDR; + buf->flags[0] = permutation & 0xFFFFFFFF; + buf->flags[1] = (permutation >> 32) | (stride << 16) | (attribCount << 28); + return id; +} + +static C3D_BufInfo* C3D_GetBufInfo(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + ctx->flags |= C3DiF_BufInfo; + return &ctx->bufInfo; +} + +static void C3Di_BufInfoBind(C3D_BufInfo* info) +{ + GPUCMD_AddWrite(GPUREG_ATTRIBBUFFERS_LOC, BUFFER_BASE_PADDR >> 3); + GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFER0_OFFSET, (u32*)info->buffers, sizeof(info->buffers)/sizeof(u32)); +} + + + + + + + + + +static void C3D_DrawElements(GPU_Primitive_t primitive, int count, int type, const void* indices) +{ + u32 pa = osConvertVirtToPhys(indices); + if (pa < BUFFER_BASE_PADDR) return; + + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive != GPU_TRIANGLES ? primitive : GPU_GEOMETRY_PRIM); + // Start a new primitive (breaks off a triangle strip/fan) + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); + // Configure the index buffer + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, (pa - BUFFER_BASE_PADDR) | (type << 31)); + // Number of vertices + GPUCMD_AddWrite(GPUREG_NUMVERTICES, count); + // First vertex + GPUCMD_AddWrite(GPUREG_VERTEX_OFFSET, 0); + // Enable triangle element drawing mode if necessary + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0x100); + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0x100); + // Enable drawing mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); + // Trigger element drawing + GPUCMD_AddWrite(GPUREG_DRAWELEMENTS, 1); + // Go back to configuration mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); + // Disable triangle element drawing mode if necessary + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG, 2, 0); + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 2, 0); + // Clear the post-vertex cache + GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x8, 0); + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x8, 0); + + C3Di_GetContext()->flags |= C3DiF_DrawUsed; +} + + + + + + + + +static inline C3D_Effect* getEffect() +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Effect; + return &ctx->effect; +} + +static void C3D_DepthMap(bool bIsZBuffer, float zScale, float zOffset) +{ + C3D_Effect* e = getEffect(); + e->zBuffer = bIsZBuffer; + e->zScale = f32tof24(zScale); + e->zOffset = f32tof24(zOffset); +} + +static void C3D_CullFace(GPU_CULLMODE mode) +{ + C3D_Effect* e = getEffect(); + e->cullMode = mode; +} + +static void C3D_StencilTest(void) +{ + C3D_Effect* e = getEffect(); + e->stencilMode = false | (GPU_ALWAYS << 4) | (0xFF << 24); +} + +static void C3D_StencilOp(void) +{ + C3D_Effect* e = getEffect(); + e->stencilOp = GPU_STENCIL_KEEP | (GPU_STENCIL_KEEP << 4) | (GPU_STENCIL_KEEP << 8); +} + +static void C3D_EarlyDepthTest(bool enable, GPU_EARLYDEPTHFUNC function, u32 ref) +{ + C3D_Effect* e = getEffect(); + e->earlyDepth = enable; + e->earlyDepthFunc = function; + e->earlyDepthRef = ref; +} + +static void C3D_DepthTest(bool enable, GPU_TESTFUNC function, GPU_WRITEMASK writemask) +{ + C3D_Effect* e = getEffect(); + e->depthTest = (!!enable) | ((function & 7) << 4) | (writemask << 8); +} + +static void C3D_AlphaTest(bool enable, GPU_TESTFUNC function, int ref) +{ + C3D_Effect* e = getEffect(); + e->alphaTest = (!!enable) | ((function & 7) << 4) | (ref << 8); +} + +static void C3D_AlphaBlend(GPU_BLENDEQUATION colorEq, GPU_BLENDEQUATION alphaEq, GPU_BLENDFACTOR srcClr, GPU_BLENDFACTOR dstClr, GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstAlpha) +{ + C3D_Effect* e = getEffect(); + e->alphaBlend = colorEq | (alphaEq << 8) | (srcClr << 16) | (dstClr << 20) | (srcAlpha << 24) | (dstAlpha << 28); + e->fragOpMode &= ~0xFF00; + e->fragOpMode |= 0x0100; +} + +static void C3D_ColorLogicOp(GPU_LOGICOP op) +{ + C3D_Effect* e = getEffect(); + e->fragOpMode &= ~0xFF00; + e->clrLogicOp = op; +} + +static void C3D_FragOpMode(GPU_FRAGOPMODE mode) +{ + C3D_Effect* e = getEffect(); + e->fragOpMode &= ~0xFF00FF; + e->fragOpMode |= 0xE40000 | mode; +} + +static void C3D_FragOpShadow(float scale, float bias) +{ + C3D_Effect* e = getEffect(); + e->fragOpShadow = f32tof16(scale+bias) | (f32tof16(-scale)<<16); +} + +static void C3Di_EffectBind(C3D_Effect* e) +{ + GPUCMD_AddWrite(GPUREG_DEPTHMAP_ENABLE, e->zBuffer ? 1 : 0); + GPUCMD_AddWrite(GPUREG_FACECULLING_CONFIG, e->cullMode & 0x3); + GPUCMD_AddIncrementalWrites(GPUREG_DEPTHMAP_SCALE, (u32*)&e->zScale, 2); + GPUCMD_AddIncrementalWrites(GPUREG_FRAGOP_ALPHA_TEST, (u32*)&e->alphaTest, 4); + GPUCMD_AddMaskedWrite(GPUREG_GAS_DELTAZ_DEPTH, 0x8, (u32)GPU_MAKEGASDEPTHFUNC((e->depthTest>>4)&7) << 24); + GPUCMD_AddWrite(GPUREG_BLEND_COLOR, 0); + GPUCMD_AddWrite(GPUREG_BLEND_FUNC, e->alphaBlend); + GPUCMD_AddWrite(GPUREG_LOGIC_OP, e->clrLogicOp); + GPUCMD_AddMaskedWrite(GPUREG_COLOR_OPERATION, 7, e->fragOpMode); + GPUCMD_AddWrite(GPUREG_FRAGOP_SHADOW, e->fragOpShadow); + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 1, e->earlyDepth ? 1 : 0); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, e->earlyDepth ? 1 : 0); + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_FUNC, 1, e->earlyDepthFunc); + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_DATA, 0x7, e->earlyDepthRef); +} + + + + + + + +static void FogLut_FromArray(C3D_FogLut* lut, const float data[256]) +{ + int i; + for (i = 0; i < 128; i ++) + { + float in = data[i], diff = data[i+128]; + + u32 val = 0; + if (in > 0.0f) + { + in *= 0x800; + val = (in < 0x800) ? (u32)in : 0x7FF; + } + + u32 val2 = 0; + if (diff != 0.0f) + { + diff *= 0x800; + if (diff < -0x1000) diff = -0x1000; + else if (diff > 0xFFF) diff = 0xFFF; + val2 = (s32)diff & 0x1FFF; + } + + lut->data[i] = val2 | (val << 13); + } +} + +static void FogLut_Exp(C3D_FogLut* lut, float density, float gradient, float near, float far) +{ + int i; + float data[256]; + for (i = 0; i <= 128; i ++) + { + float x = FogLut_CalcZ(i/128.0f, near, far); + float val = expf(-powf(density*x, gradient)); + if (i < 128) + data[i] = val; + if (i > 0) + data[i+127] = val-data[i-1]; + } + FogLut_FromArray(lut, data); +} + +static void C3D_FogGasMode(GPU_FOGMODE fogMode, GPU_GASMODE gasMode, bool zFlip) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->flags |= C3DiF_TexEnvBuf; + ctx->texEnvBuf &= ~0x100FF; + ctx->texEnvBuf |= (fogMode&7) | ((gasMode&1)<<3) | (zFlip ? BIT(16) : 0); +} + +static void C3D_FogColor(u32 color) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + ctx->flags |= C3DiF_TexEnvBuf; + ctx->fogClr = color; +} + +static void C3D_FogLutBind(C3D_FogLut* lut) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (lut) + { + ctx->flags |= C3DiF_FogLut; + ctx->fogLut = lut; + } else + ctx->flags &= ~C3DiF_FogLut; +} + + + + + + + + + + +static const u8 colorFmtSizes[] = {2,1,0,0,0}; +static const u8 depthFmtSizes[] = {0,0,1,2}; + +static u32 C3D_CalcColorBufSize(u32 width, u32 height, GPU_COLORBUF fmt) +{ + u32 size = width*height; + return size*(2+colorFmtSizes[fmt]); +} + +static u32 C3D_CalcDepthBufSize(u32 width, u32 height, GPU_DEPTHBUF fmt) +{ + u32 size = width*height; + return size*(2+depthFmtSizes[fmt]); +} + +static C3D_FrameBuf* C3D_GetFrameBuf(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + ctx->flags |= C3DiF_FrameBuf; + return &ctx->fb; +} + +static void C3D_SetFrameBuf(C3D_FrameBuf* fb) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + if (fb != &ctx->fb) + memcpy(&ctx->fb, fb, sizeof(*fb)); + ctx->flags |= C3DiF_FrameBuf; +} + +static void C3Di_FrameBufBind(C3D_FrameBuf* fb) +{ + u32 param[4] = { 0, 0, 0, 0 }; + + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); + + param[0] = osConvertVirtToPhys(fb->depthBuf) >> 3; + param[1] = osConvertVirtToPhys(fb->colorBuf) >> 3; + param[2] = 0x01000000 | (((u32)(fb->height-1) & 0xFFF) << 12) | (fb->width & 0xFFF); + GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, param, 3); + + GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, param[2]); + GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, fb->depthFmt); + GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, colorFmtSizes[fb->colorFmt] | ((u32)fb->colorFmt << 16)); + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, fb->block32 ? 1 : 0); + + // Enable or disable color/depth buffers + param[0] = param[1] = fb->colorBuf ? fb->colorMask : 0; + param[2] = param[3] = fb->depthBuf ? fb->depthMask : 0; + GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, param, 4); +} + +static void C3D_FrameBufClear(C3D_FrameBuf* frameBuf, C3D_ClearBits clearBits, u32 clearColor, u32 clearDepth) +{ + u32 size = (u32)frameBuf->width * frameBuf->height; + u32 cfs = colorFmtSizes[frameBuf->colorFmt]; + u32 dfs = depthFmtSizes[frameBuf->depthFmt]; + void* colorBufEnd = (u8*)frameBuf->colorBuf + size*(2+cfs); + void* depthBufEnd = (u8*)frameBuf->depthBuf + size*(2+dfs); + + if (clearBits & C3D_CLEAR_COLOR) + { + if (clearBits & C3D_CLEAR_DEPTH) + GX_MemoryFill( + (u32*)frameBuf->colorBuf, clearColor, (u32*)colorBufEnd, BIT(0) | (cfs << 8), + (u32*)frameBuf->depthBuf, clearDepth, (u32*)depthBufEnd, BIT(0) | (dfs << 8)); + else + GX_MemoryFill( + (u32*)frameBuf->colorBuf, clearColor, (u32*)colorBufEnd, BIT(0) | (cfs << 8), + NULL, 0, NULL, 0); + } else + GX_MemoryFill( + (u32*)frameBuf->depthBuf, clearDepth, (u32*)depthBufEnd, BIT(0) | (dfs << 8), + NULL, 0, NULL, 0); +} + +static void C3D_FrameBufTransfer(C3D_FrameBuf* frameBuf, gfxScreen_t screen, gfx3dSide_t side, u32 transferFlags) +{ + u32* outputFrameBuf = (u32*)gfxGetFramebuffer(screen, side, NULL, NULL); + u32 dim = GX_BUFFER_DIM((u32)frameBuf->width, (u32)frameBuf->height); + GX_DisplayTransfer((u32*)frameBuf->colorBuf, dim, outputFrameBuf, dim, transferFlags); +} + + + + + + + +static void C3D_ImmDrawBegin(GPU_Primitive_t primitive) +{ + C3Di_UpdateContext(); + + // Set primitive type + GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 2, primitive); + // Start a new primitive (breaks off a triangle strip/fan) + GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1); + // Not sure if this command is necessary + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, 0x80000000); + // Enable vertex submission mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 1); + // Enable drawing mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 0); + // Begin immediate-mode vertex submission + GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, 0xF); +} + +static inline void write24(u8* p, u32 val) +{ + p[0] = val; + p[1] = val>>8; + p[2] = val>>16; +} + +static void C3D_ImmSendAttrib(float x, float y, float z, float w) +{ + union + { + u32 packed[3]; + struct + { + u8 x[3]; + u8 y[3]; + u8 z[3]; + u8 w[3]; + }; + } param; + + // Convert the values to float24 + write24(param.x, f32tof24(x)); + write24(param.y, f32tof24(y)); + write24(param.z, f32tof24(z)); + write24(param.w, f32tof24(w)); + + // Reverse the packed words + u32 p = param.packed[0]; + param.packed[0] = param.packed[2]; + param.packed[2] = p; + + // Send the attribute + GPUCMD_AddIncrementalWrites(GPUREG_FIXEDATTRIB_DATA0, param.packed, 3); +} + +static void C3D_ImmDrawEnd(void) +{ + // Go back to configuration mode + GPUCMD_AddMaskedWrite(GPUREG_START_DRAW_FUNC0, 1, 1); + // Disable vertex submission mode + GPUCMD_AddMaskedWrite(GPUREG_GEOSTAGE_CONFIG2, 1, 0); + // Clear the post-vertex cache + GPUCMD_AddWrite(GPUREG_VTX_FUNC, 1); + + C3Di_GetContext()->flags |= C3DiF_DrawUsed; +} + + + +static C3D_RenderTarget *firstTarget, *lastTarget; +static C3D_RenderTarget *linkedTarget[3]; + +static bool inFrame, inSafeTransfer; +static bool needSwapTop, needSwapBot, isTopStereo; +static float framerate = 60.0f; +static float framerateCounter[2] = { 60.0f, 60.0f }; +static u32 frameCounter[2]; + +static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target); + +static bool framerateLimit(int id) +{ + framerateCounter[id] -= framerate; + if (framerateCounter[id] <= 0.0f) + { + framerateCounter[id] += 60.0f; + return true; + } + return false; +} + +static void onVBlank0(void* unused) +{ + if (framerateLimit(0)) + frameCounter[0]++; +} + +static void onVBlank1(void* unused) +{ + if (framerateLimit(1)) + frameCounter[1]++; +} + +static void onQueueFinish(gxCmdQueue_s* queue) +{ + if (inSafeTransfer) + { + inSafeTransfer = false; + if (inFrame) + { + gxCmdQueueStop(queue); + gxCmdQueueClear(queue); + } + } + else + { + if (needSwapTop) + { + gfxScreenSwapBuffers(GFX_TOP, isTopStereo); + needSwapTop = false; + } + if (needSwapBot) + { + gfxScreenSwapBuffers(GFX_BOTTOM, false); + needSwapBot = false; + } + } +} + +static void C3D_FrameSync(void) +{ + u32 cur[2]; + u32 start[2] = { frameCounter[0], frameCounter[1] }; + do + { + gspWaitForAnyEvent(); + cur[0] = frameCounter[0]; + cur[1] = frameCounter[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); +} + +static void C3Di_RenderQueueExit(void) +{ + int i; + C3D_RenderTarget *a, *next; + + C3Di_WaitAndClearQueue(-1); + gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL); + GX_BindQueue(NULL); + + C3Di_RenderQueueDisableVBlank(); + + for (i = 0; i < 3; i ++) + linkedTarget[i] = NULL; + + for (a = firstTarget; a; a = next) + { + next = a->next; + C3Di_RenderTargetDestroy(a); + } +} + +static void C3Di_RenderQueueWaitDone(void) +{ + C3Di_WaitAndClearQueue(-1); +} + +static bool C3D_FrameBegin(u8 flags) +{ + C3D_Context* ctx = C3Di_GetContext(); + if (inFrame) return false; + + if (flags & C3D_FRAME_SYNCDRAW) + C3D_FrameSync(); + if (!C3Di_WaitAndClearQueue((flags & C3D_FRAME_NONBLOCK) ? 0 : -1)) + return false; + + inFrame = true; + GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0); + return true; +} + +static bool C3D_FrameDrawOn(C3D_RenderTarget* target) +{ + if (!inFrame) return false; + + target->used = true; + C3D_SetFrameBuf(&target->frameBuf); + C3D_SetViewport(0, 0, target->frameBuf.width, target->frameBuf.height); + return true; +} + +static void C3D_FrameSplit(u8 flags) +{ + u32 *cmdBuf, cmdBufSize; + if (!inFrame) return; + if (C3Di_SplitFrame(&cmdBuf, &cmdBufSize)) + GX_ProcessCommandList(cmdBuf, cmdBufSize*4, flags); +} + +static void C3D_FrameEnd(u8 flags) +{ + C3D_Context* ctx = C3Di_GetContext(); + if (!inFrame) return; + + C3D_FrameSplit(flags); + GPUCMD_SetBuffer(NULL, 0, 0); + inFrame = false; + + // 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; + GSPGPU_FlushDataCache((void*)__ctru_linear_heap, __ctru_linear_heap_size); + } + + int i; + C3D_RenderTarget* target; + isTopStereo = false; + for (i = 2; i >= 0; i --) + { + target = linkedTarget[i]; + if (!target || !target->used) + continue; + target->used = false; + C3D_FrameBufTransfer(&target->frameBuf, target->screen, target->side, target->transferFlags); + if (target->screen == GFX_TOP) + { + needSwapTop = true; + if (target->side == GFX_RIGHT) + isTopStereo = true; + } + else if (target->screen == GFX_BOTTOM) + needSwapBot = true; + } + + gxCmdQueueRun(&ctx->gxQueue); +} + +static C3D_RenderTarget* C3Di_RenderTargetNew(void) +{ + C3D_RenderTarget* target = (C3D_RenderTarget*)malloc(sizeof(C3D_RenderTarget)); + if (!target) return NULL; + memset(target, 0, sizeof(C3D_RenderTarget)); + return target; +} + +static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target) +{ + target->prev = lastTarget; + target->next = NULL; + if (lastTarget) + lastTarget->next = target; + if (!firstTarget) + firstTarget = target; + lastTarget = target; +} + +C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, GPU_DEPTHBUF depthFmt) +{ + void* depthBuf = NULL; + void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt)); + if (!colorBuf) goto _fail0; + + size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmt); + 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 + if (!depthBuf) goto _fail1; + + 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); + C3D_FrameBufDepth(fb, depthBuf, depthFmt); + target->ownsDepth = true; + target->ownsColor = true; + + C3Di_RenderTargetFinishInit(target); + return target; + +_fail2: + if (depthBuf) vramFree(depthBuf); +_fail1: + vramFree(colorBuf); +_fail0: + return NULL; +} + +static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target) +{ + if (target->ownsColor) + vramFree(target->frameBuf.colorBuf); + if (target->ownsDepth) + vramFree(target->frameBuf.depthBuf); + + 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); +} + +static void C3D_RenderTargetDelete(C3D_RenderTarget* target) +{ + if (inFrame) + svcBreak(USERBREAK_PANIC); // Shouldn't happen. + if (target->linked) + C3D_RenderTargetDetachOutput(target); + else + C3Di_WaitAndClearQueue(-1); + C3Di_RenderTargetDestroy(target); +} + +static 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; + if (!inFrame) + C3Di_WaitAndClearQueue(-1); + } + linkedTarget[id] = target; + if (target) + { + target->linked = true; + target->transferFlags = transferFlags; + target->screen = screen; + target->side = side; + } +} + +static void C3Di_SafeTextureCopy(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 size, u32 flags) +{ + C3Di_WaitAndClearQueue(-1); + inSafeTransfer = true; + GX_TextureCopy(inadr, indim, outadr, outdim, size, flags); + gxCmdQueueRun(&C3Di_GetContext()->gxQueue); +} + +static 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(); + } +} + + + + + + + +static void C3Di_TexEnvBind(int id, C3D_TexEnv* env) +{ + if (id >= 4) id += 2; + GPUCMD_AddIncrementalWrites(GPUREG_TEXENV0_SOURCE + id*8, (u32*)env, sizeof(C3D_TexEnv)/sizeof(u32)); +} + + + + + +// Return bits per pixel +static inline size_t fmtSize(GPU_TEXCOLOR fmt) +{ + switch (fmt) + { + case GPU_RGBA8: + return 32; + case GPU_RGB8: + return 24; + case GPU_RGBA5551: + case GPU_RGB565: + case GPU_RGBA4: + return 16; + default: + return 0; + } +} + +static bool C3D_TexInitWithParams(C3D_Tex* tex, void* cube, C3D_TexInitParams p) +{ + u32 size = fmtSize(p.format); + if (!size) return false; + size *= (u32)p.width * p.height / 8; + u32 total_size = C3D_TexCalcTotalSize(size, p.maxLevel); + + tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); + if (!tex->data) return false; + + tex->width = p.width; + tex->height = p.height; + tex->param = GPU_TEXTURE_MODE(GPU_TEX_2D); + tex->fmt = p.format; + tex->size = size; + + tex->border = 0; + tex->lodBias = 0; + tex->maxLevel = p.maxLevel; + tex->minLevel = 0; + return true; +} + +static void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level) +{ + u32 size = 0; + void* out = C3D_TexGetImagePtr(tex, + tex->data, + level, &size); + + if (!addrIsVRAM(out)) + memcpy(out, data, size); + else + C3D_SyncTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8); +} + +static void C3D_TexBind(int unitId, C3D_Tex* tex) +{ + C3D_Context* ctx = C3Di_GetContext(); + + ctx->flags |= C3DiF_Tex(unitId); + ctx->tex[unitId] = tex; +} + +static void C3D_TexFlush(C3D_Tex* tex) +{ + if (!addrIsVRAM(tex->data)) + GSPGPU_FlushDataCache(tex->data, C3D_TexCalcTotalSize(tex->size, tex->maxLevel)); +} + +static void C3D_TexDelete(C3D_Tex* tex) +{ + void* addr = tex->data; + if (addrIsVRAM(addr)) + vramFree(addr); + else + linearFree(addr); +} + +static void C3Di_SetTex(int unit, C3D_Tex* tex) +{ + u32 reg[10]; + u32 regcount = 5; + reg[0] = tex->border; + reg[1] = tex->dim; + reg[2] = tex->param; + reg[3] = tex->lodParam; + reg[4] = osConvertVirtToPhys(tex->data) >> 3; + + switch (unit) + { + case 0: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, regcount); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt); + break; + case 1: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt); + break; + case 2: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt); + break; + } +} + + + + + + +static C3D_FVec C3D_FVUnif[2][C3D_FVUNIF_COUNT]; +static C3D_IVec C3D_IVUnif[2][C3D_IVUNIF_COUNT]; +static u16 C3D_BoolUnifs[2]; + +static bool C3D_FVUnifDirty[2][C3D_FVUNIF_COUNT]; +static bool C3D_IVUnifDirty[2][C3D_IVUNIF_COUNT]; +static bool C3D_BoolUnifsDirty[2]; + +static struct +{ + bool dirty; + int count; + float24Uniform_s* data; +} C3Di_ShaderFVecData[2]; + +static bool C3Di_FVUnifEverDirty[2][C3D_FVUNIF_COUNT]; +static bool C3Di_IVUnifEverDirty[2][C3D_IVUNIF_COUNT]; + +static void C3D_UpdateUniforms(GPU_SHADER_TYPE type) +{ + int offset = type == GPU_GEOMETRY_SHADER ? (GPUREG_GSH_BOOLUNIFORM-GPUREG_VSH_BOOLUNIFORM) : 0; + int i = 0; + + // Update FVec uniforms that come from shader constants + if (C3Di_ShaderFVecData[type].dirty) + { + while (i < C3Di_ShaderFVecData[type].count) + { + float24Uniform_s* u = &C3Di_ShaderFVecData[type].data[i++]; + GPUCMD_AddIncrementalWrites(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, (u32*)u, 4); + C3D_FVUnifDirty[type][u->id] = false; + } + C3Di_ShaderFVecData[type].dirty = false; + i = 0; + } + + // Update FVec uniforms + while (i < C3D_FVUNIF_COUNT) + { + if (!C3D_FVUnifDirty[type][i]) + { + i ++; + continue; + } + + // Find the number of consecutive dirty uniforms + int j; + for (j = i; j < C3D_FVUNIF_COUNT && C3D_FVUnifDirty[type][j]; j ++); + + // Upload the uniforms + GPUCMD_AddWrite(GPUREG_VSH_FLOATUNIFORM_CONFIG+offset, 0x80000000|i); + GPUCMD_AddWrites(GPUREG_VSH_FLOATUNIFORM_DATA+offset, (u32*)&C3D_FVUnif[type][i], (j-i)*4); + + // Clear the dirty flag + int k; + for (k = i; k < j; k ++) + { + C3D_FVUnifDirty[type][k] = false; + C3Di_FVUnifEverDirty[type][k] = true; + } + + // Advance + i = j; + } + + // Update IVec uniforms + for (i = 0; i < C3D_IVUNIF_COUNT; i ++) + { + if (!C3D_IVUnifDirty[type][i]) continue; + + GPUCMD_AddWrite(GPUREG_VSH_INTUNIFORM_I0+offset+i, C3D_IVUnif[type][i]); + C3D_IVUnifDirty[type][i] = false; + C3Di_IVUnifEverDirty[type][i] = false; + } + + // Update bool uniforms + if (C3D_BoolUnifsDirty[type]) + { + GPUCMD_AddWrite(GPUREG_VSH_BOOLUNIFORM+offset, 0x7FFF0000 | C3D_BoolUnifs[type]); + C3D_BoolUnifsDirty[type] = false; + } +} + +static void C3Di_DirtyUniforms(GPU_SHADER_TYPE type) +{ + int i; + C3D_BoolUnifsDirty[type] = true; + if (C3Di_ShaderFVecData[type].count) + C3Di_ShaderFVecData[type].dirty = true; + for (i = 0; i < C3D_FVUNIF_COUNT; i ++) + C3D_FVUnifDirty[type][i] = C3D_FVUnifDirty[type][i] || C3Di_FVUnifEverDirty[type][i]; + for (i = 0; i < C3D_IVUNIF_COUNT; i ++) + C3D_IVUnifDirty[type][i] = C3D_IVUnifDirty[type][i] || C3Di_IVUnifEverDirty[type][i]; +} + +static void C3Di_LoadShaderUniforms(shaderInstance_s* si) +{ + GPU_SHADER_TYPE type = si->dvle->type; + if (si->boolUniformMask) + { + C3D_BoolUnifs[type] &= ~si->boolUniformMask; + C3D_BoolUnifs[type] |= si->boolUniforms; + } + + if (type == GPU_GEOMETRY_SHADER) + C3D_BoolUnifs[type] &= ~BIT(15); + C3D_BoolUnifsDirty[type] = true; + + if (si->intUniformMask) + { + int i; + for (i = 0; i < 4; i ++) + { + if (si->intUniformMask & BIT(i)) + { + C3D_IVUnif[type][i] = si->intUniforms[i]; + C3D_IVUnifDirty[type][i] = true; + } + } + } + C3Di_ShaderFVecData[type].dirty = true; + C3Di_ShaderFVecData[type].count = si->numFloat24Uniforms; + C3Di_ShaderFVecData[type].data = si->float24Uniforms; +} + +static void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type) +{ + C3Di_ShaderFVecData[type].dirty = false; + C3Di_ShaderFVecData[type].count = 0; + C3Di_ShaderFVecData[type].data = NULL; +} + + + + + + + + + + + + + + + +static aptHookCookie hookCookie; + +static void C3Di_AptEventHook(APT_HookType hookType, void* param) +{ + C3D_Context* ctx = C3Di_GetContext(); + + switch (hookType) + { + case APTHOOK_ONSUSPEND: + { + C3Di_RenderQueueWaitDone(); + C3Di_RenderQueueDisableVBlank(); + break; + } + case APTHOOK_ONRESTORE: + { + C3Di_RenderQueueEnableVBlank(); + ctx->flags |= C3DiF_AttrInfo | C3DiF_BufInfo | C3DiF_Effect | C3DiF_FrameBuf + | C3DiF_Viewport | C3DiF_Scissor | C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode + | C3DiF_TexAll | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_LightEnv | C3DiF_Gas; + + C3Di_DirtyUniforms(GPU_VERTEX_SHADER); + C3Di_DirtyUniforms(GPU_GEOMETRY_SHADER); + + if (ctx->fogLut) + ctx->flags |= C3DiF_FogLut; + break; + } + default: + break; + } +} + +static bool C3D_Init(size_t cmdBufSize) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_Active) + return false; + + cmdBufSize = (cmdBufSize + 0xF) &~ 0xF; // 0x10-byte align + ctx->cmdBufSize = cmdBufSize/4; + ctx->cmdBuf = (u32*)linearAlloc(cmdBufSize); + if (!ctx->cmdBuf) + return false; + + ctx->gxQueue.maxEntries = 32; + ctx->gxQueue.entries = (gxCmdEntry_s*)malloc(ctx->gxQueue.maxEntries*sizeof(gxCmdEntry_s)); + if (!ctx->gxQueue.entries) + { + linearFree(ctx->cmdBuf); + return false; + } + + ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexStatus | C3DiF_TexAll; + + // TODO: replace with direct struct access + C3D_DepthMap(true, -1.0f, 0.0f); + C3D_CullFace(GPU_CULL_BACK_CCW); + C3D_StencilTest(); + C3D_StencilOp(); + C3D_EarlyDepthTest(false, GPU_EARLYDEPTH_GREATER, 0); + C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); + C3D_AlphaTest(false, GPU_ALWAYS, 0x00); + C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + C3D_FragOpMode(GPU_FRAGOPMODE_GL); + C3D_FragOpShadow(0.0, 1.0); + + ctx->texConfig = BIT(12); + ctx->texShadow = BIT(0); + ctx->texEnvBuf = 0; + ctx->texEnvBufClr = 0xFFFFFFFF; + ctx->fogClr = 0; + ctx->fogLut = NULL; + + for (i = 0; i < 3; i ++) + ctx->tex[i] = NULL; + + for (i = 0; i < 6; i ++) + C3D_TexEnvInit(&ctx->texEnv[i]); + + C3Di_RenderQueueInit(); + aptHook(&hookCookie, C3Di_AptEventHook, NULL); + + return true; +} + +static void C3D_SetViewport(u32 x, u32 y, u32 w, u32 h) +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Viewport | C3DiF_Scissor; + ctx->viewport[0] = f32tof24(w / 2.0f); + ctx->viewport[1] = f32tof31(2.0f / w) << 1; + ctx->viewport[2] = f32tof24(h / 2.0f); + ctx->viewport[3] = f32tof31(2.0f / h) << 1; + ctx->viewport[4] = (y << 16) | (x & 0xFFFF); + ctx->scissor[0] = GPU_SCISSOR_DISABLE; +} + +static void C3D_SetScissor(GPU_SCISSORMODE mode, u32 left, u32 top, u32 right, u32 bottom) +{ + C3D_Context* ctx = C3Di_GetContext(); + ctx->flags |= C3DiF_Scissor; + ctx->scissor[0] = mode; + if (mode == GPU_SCISSOR_DISABLE) return; + ctx->scissor[1] = (top << 16) | (left & 0xFFFF); + ctx->scissor[2] = ((bottom-1) << 16) | ((right-1) & 0xFFFF); +} + +static void C3Di_UpdateContext(void) +{ + int i; + C3D_Context* ctx = C3Di_GetContext(); + + if (ctx->flags & C3DiF_FrameBuf) + { + ctx->flags &= ~C3DiF_FrameBuf; + if (ctx->flags & C3DiF_DrawUsed) + { + ctx->flags &= ~C3DiF_DrawUsed; + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); + } + C3Di_FrameBufBind(&ctx->fb); + } + + if (ctx->flags & C3DiF_Viewport) + { + ctx->flags &= ~C3DiF_Viewport; + GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, ctx->viewport, 4); + GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, ctx->viewport[4]); + } + + if (ctx->flags & C3DiF_Scissor) + { + ctx->flags &= ~C3DiF_Scissor; + GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, ctx->scissor, 3); + } + + if (ctx->flags & C3DiF_Program) + { + shaderProgramConfigure(ctx->program, (ctx->flags & C3DiF_VshCode) != 0, (ctx->flags & C3DiF_GshCode) != 0); + ctx->flags &= ~(C3DiF_Program | C3DiF_VshCode | C3DiF_GshCode); + } + + if (ctx->flags & C3DiF_AttrInfo) + { + ctx->flags &= ~C3DiF_AttrInfo; + C3Di_AttrInfoBind(&ctx->attrInfo); + } + + if (ctx->flags & C3DiF_BufInfo) + { + ctx->flags &= ~C3DiF_BufInfo; + C3Di_BufInfoBind(&ctx->bufInfo); + } + + if (ctx->flags & C3DiF_Effect) + { + ctx->flags &= ~C3DiF_Effect; + C3Di_EffectBind(&ctx->effect); + } + + if (ctx->flags & C3DiF_TexAll) + { + u32 units = 0; + for (i = 0; i < 3; i ++) + { + if (ctx->tex[i]) + { + units |= BIT(i); + if (ctx->flags & C3DiF_Tex(i)) + C3Di_SetTex(i, ctx->tex[i]); + } + } + + // Enable texture units and clear texture cache + ctx->texConfig &= ~7; + ctx->texConfig |= units | BIT(16); + ctx->flags &= ~C3DiF_TexAll; + ctx->flags |= C3DiF_TexStatus; + } + + if (ctx->flags & C3DiF_TexStatus) + { + ctx->flags &= ~C3DiF_TexStatus; + GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0xB, ctx->texConfig); + // Clear texture cache if requested *after* configuring texture units + if (ctx->texConfig & BIT(16)) + { + ctx->texConfig &= ~BIT(16); + GPUCMD_AddMaskedWrite(GPUREG_TEXUNIT_CONFIG, 0x4, BIT(16)); + } + GPUCMD_AddWrite(GPUREG_TEXUNIT0_SHADOW, ctx->texShadow); + } + + if (ctx->flags & C3DiF_TexEnvBuf) + { + ctx->flags &= ~C3DiF_TexEnvBuf; + GPUCMD_AddMaskedWrite(GPUREG_TEXENV_UPDATE_BUFFER, 0x7, ctx->texEnvBuf); + GPUCMD_AddWrite(GPUREG_TEXENV_BUFFER_COLOR, ctx->texEnvBufClr); + GPUCMD_AddWrite(GPUREG_FOG_COLOR, ctx->fogClr); + } + + if ((ctx->flags & C3DiF_FogLut) && (ctx->texEnvBuf&7) != GPU_NO_FOG) + { + ctx->flags &= ~C3DiF_FogLut; + if (ctx->fogLut) + { + GPUCMD_AddWrite(GPUREG_FOG_LUT_INDEX, 0); + GPUCMD_AddWrites(GPUREG_FOG_LUT_DATA0, ctx->fogLut->data, 128); + } + } + + if (ctx->flags & C3DiF_TexEnvAll) + { + for (i = 0; i < 6; i ++) + { + if (!(ctx->flags & C3DiF_TexEnv(i))) continue; + C3Di_TexEnvBind(i, &ctx->texEnv[i]); + } + ctx->flags &= ~C3DiF_TexEnvAll; + } + + if (ctx->flags & C3DiF_LightEnv) + { + u32 enable = false; + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE0, enable); + GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE1, !enable); + ctx->flags &= ~C3DiF_LightEnv; + } + + C3D_UpdateUniforms(GPU_VERTEX_SHADER); + C3D_UpdateUniforms(GPU_GEOMETRY_SHADER); +} + +static bool C3Di_SplitFrame(u32** pBuf, u32* pSize) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!gpuCmdBufOffset) + return false; // Nothing was drawn + + if (ctx->flags & C3DiF_DrawUsed) + { + ctx->flags &= ~C3DiF_DrawUsed; + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 1); + GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 1); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_CLEAR, 1); + } + + GPUCMD_Split(pBuf, pSize); + return true; +} + +static void C3D_Fini(void) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + aptUnhook(&hookCookie); + C3Di_RenderQueueExit(); + free(ctx->gxQueue.entries); + linearFree(ctx->cmdBuf); + ctx->flags = 0; +} + +static void C3D_BindProgram(shaderProgram_s* program) +{ + C3D_Context* ctx = C3Di_GetContext(); + + if (!(ctx->flags & C3DiF_Active)) + return; + + shaderProgram_s* oldProg = ctx->program; + shaderInstance_s* newGsh = program->geometryShader; + if (oldProg != program) + { + ctx->program = program; + ctx->flags |= C3DiF_Program | C3DiF_AttrInfo; + + if (!oldProg) + ctx->flags |= C3DiF_VshCode | C3DiF_GshCode; + else + { + shaderInstance_s* oldGsh = oldProg->geometryShader; + + DVLP_s* oldProgV = oldProg->vertexShader->dvle->dvlp; + DVLP_s* oldProgG = oldGsh ? oldGsh->dvle->dvlp : oldProgV; + + DVLP_s* newProgV = program->vertexShader->dvle->dvlp; + DVLP_s* newProgG = newGsh ? newGsh->dvle->dvlp : newProgV; + + if (oldProgV != newProgV || (!newGsh && oldProgG != newProgG)) + ctx->flags |= C3DiF_VshCode; + if (oldProgG != newProgG || (newProgG==oldProgV && newProgG->codeSize >= 512)) + ctx->flags |= C3DiF_GshCode; + } + } + + C3Di_LoadShaderUniforms(program->vertexShader); + if (newGsh) + C3Di_LoadShaderUniforms(newGsh); + else + C3Di_ClearShaderUniforms(GPU_GEOMETRY_SHADER); +}