#include "internal.h" #include #include #include #include C3D_Context __C3D_Context; static aptHookCookie hookCookie; __attribute__((weak)) void C3Di_LightEnvUpdate(C3D_LightEnv* env) { (void)env; } __attribute__((weak)) void C3Di_LightEnvDirty(C3D_LightEnv* env) { (void)env; } __attribute__((weak)) void C3Di_ProcTexUpdate(C3D_Context* ctx) { (void)ctx; } __attribute__((weak)) void C3Di_ProcTexDirty(C3D_Context* ctx) { (void)ctx; } __attribute__((weak)) void C3Di_GasUpdate(C3D_Context* ctx) { (void)ctx; } static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED 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); ctx->fixedAttribDirty |= ctx->fixedAttribEverDirty; ctx->gasFlags |= C3DiG_BeginAcc | C3DiG_AccStage | C3DiG_RenderStage; C3D_LightEnv* env = ctx->lightEnv; if (ctx->fogLut) ctx->flags |= C3DiF_FogLut; if (ctx->gasLut) ctx->flags |= C3DiF_GasLut; if (env) C3Di_LightEnvDirty(env); C3Di_ProcTexDirty(ctx); break; } default: break; } } 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); ctx->cmdBufUsage = 0; 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(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); C3D_BlendingColor(0); 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]); ctx->fixedAttribDirty = 0; ctx->fixedAttribEverDirty = 0; C3Di_RenderQueueInit(); aptHook(&hookCookie, C3Di_AptEventHook, NULL); return true; } 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; } 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); } 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_ProcTex | C3DiF_ProcTexColorLut | C3DiF_ProcTexLutAll)) C3Di_ProcTexUpdate(ctx); 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->texEnvBuf&7) == GPU_GAS) C3Di_GasUpdate(ctx); 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; } C3D_LightEnv* env = ctx->lightEnv; if (ctx->flags & C3DiF_LightEnv) { u32 enable = env != NULL; GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE0, enable); GPUCMD_AddWrite(GPUREG_LIGHTING_ENABLE1, !enable); ctx->flags &= ~C3DiF_LightEnv; } if (env) C3Di_LightEnvUpdate(env); if (ctx->fixedAttribDirty) { for (i = 0; i < 12; i ++) { if (!(ctx->fixedAttribDirty & BIT(i))) continue; C3D_FVec* v = &ctx->fixedAttribs[i]; GPUCMD_AddWrite(GPUREG_FIXEDATTRIB_INDEX, i); C3D_ImmSendAttrib(v->x, v->y, v->z, v->w); } ctx->fixedAttribDirty = 0; } C3D_UpdateUniforms(GPU_VERTEX_SHADER); C3D_UpdateUniforms(GPU_GEOMETRY_SHADER); } 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); u32 totalCmdBufSize = *pBuf + *pSize - ctx->cmdBuf; ctx->cmdBufUsage = (float)totalCmdBufSize / ctx->cmdBufSize; return true; } float C3D_GetCmdBufUsage(void) { return C3Di_GetContext()->cmdBufUsage; } 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; } 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); } C3D_FVec* C3D_FixedAttribGetWritePtr(int id) { if (id < 0 || id >= 12) return NULL; C3D_Context* ctx = C3Di_GetContext(); if (!(ctx->flags & C3DiF_Active)) return NULL; ctx->fixedAttribDirty |= BIT(id); ctx->fixedAttribEverDirty |= BIT(id); return &ctx->fixedAttribs[id]; }