Skip to content

Instantly share code, notes, and snippets.

@smealum
Created April 26, 2014 02:29
Show Gist options
  • Save smealum/11310251 to your computer and use it in GitHub Desktop.
Save smealum/11310251 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <ctr/types.h>
#include <ctr/srv.h>
#include <ctr/APT.h>
#include <ctr/GSP.h>
#include <ctr/GX.h>
#include <ctr/GPU.h>
#include <ctr/HID.h>
#include <ctr/SHDR.h>
#include <ctr/svc.h>
#include "costable.h"
#include "test_shbin.h"
#include "test_png_bin.h"
#include "mdl.h"
u8* gspHeap;
u32* gxCmdBuf;
u8 currentBuffer;
u8* topLeftFramebuffers[2];
u8* topLeftFramebuffersPA[2];
Handle gspEvent, gspSharedMemHandle;
DVLB_s* shader;
Result GSPGPU_waitSubmitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8], Handle gspEvent, Handle* handle)
{
svc_clearEvent(gspEvent);
svc_waitSynchronization1(gspEvent, 0x55bcb0); //TODO : check output and only submit if appropriate ?
return GSPGPU_submitGxCommand(sharedGspCmdBuf, gxCommand, handle);
}
void waitGSP()
{
svc_clearEvent(gspEvent);
svc_waitSynchronization1(gspEvent, 0x55bcb0);
}
void gspGpuInit()
{
gspInit();
GSPGPU_AcquireRight(NULL, 0x0);
GSPGPU_SetLcdForceBlack(NULL, 0x0);
//set subscreen to blue
u32 regData=0x01FF0000;
GSPGPU_WriteHWRegs(NULL, 0x202A04, &regData, 4);
//grab main left screen framebuffer addresses
GSPGPU_ReadHWRegs(NULL, 0x400468, (u32*)&topLeftFramebuffersPA, 8);
//convert PA to VA (assuming FB in VRAM)
topLeftFramebuffers[0]=topLeftFramebuffersPA[0]+0x7000000;
topLeftFramebuffers[1]=topLeftFramebuffersPA[1]+0x7000000;
//setup our gsp shared mem section
u8 threadID;
svc_createEvent(&gspEvent, 0x0);
GSPGPU_RegisterInterruptRelayQueue(NULL, gspEvent, 0x1, &gspSharedMemHandle, &threadID);
svc_mapMemoryBlock(gspSharedMemHandle, 0x10002000, 0x3, 0x10000000);
//map GSP heap
svc_controlMemory((u32*)&gspHeap, 0x0, 0x0, 0x2000000, 0x10003, 0x3);
//wait until we can write stuff to it
svc_waitSynchronization1(gspEvent, 0x55bcb0);
//GSP shared mem : 0x2779F000
gxCmdBuf=(u32*)(0x10002000+0x800+threadID*0x200);
currentBuffer=0;
}
void gspGpuExit()
{
GSPGPU_UnregisterInterruptRelayQueue(NULL);
//unmap GSP shared mem
svc_unmapMemoryBlock(gspSharedMemHandle, 0x10002000);
svc_closeHandle(gspSharedMemHandle);
svc_closeHandle(gspEvent);
gspExit();
//free GSP heap
svc_controlMemory((u32*)&gspHeap, (u32)gspHeap, 0x0, 0x2000000, MEMOP_FREE, 0x0);
}
void swapBuffers()
{
u32 regData;
GSPGPU_ReadHWRegs(NULL, 0x400478, (u32*)&regData, 4);
regData^=1;
currentBuffer=regData&1;
GSPGPU_WriteHWRegs(NULL, 0x400478, (u32*)&regData, 4);
}
float* vertArray;
float* colorArray;
u16* indArray;
u32* texData;
void loadIdentity44(float* m)
{
if(!m)return;
memset(m, 0x00, 16*4);
m[0]=m[5]=m[10]=m[15]=1.0f;
}
void multMatrix44(float* m1, float* m2, float* m) //4x4
{
int i, j;
for(i=0;i<4;i++)for(j=0;j<4;j++)m[i+j*4]=(m1[0+j*4]*m2[i+0*4])+(m1[1+j*4]*m2[i+1*4])+(m1[2+j*4]*m2[i+2*4])+(m1[3+j*4]*m2[i+3*4]);
}
void translateMatrix(float* tm, float x, float y, float z)
{
float rm[16], m[16];
loadIdentity44(rm);
rm[3]=x;
rm[7]=y;
rm[11]=z;
multMatrix44(rm,tm,m);
memcpy(tm,m,16*sizeof(float));
}
void rotateMatrixX(float* tm, float x)
{
float rm[16], m[16];
memset(rm, 0x00, 16*4);
rm[0]=1.0f;
rm[5]=cos(x);
rm[6]=sin(x);
rm[9]=-sin(x);
rm[10]=cos(x);
rm[15]=1.0f;
multMatrix44(tm,rm,m);
memcpy(tm,m,16*sizeof(float));
}
void rotateMatrixZ(float* tm, float x)
{
float rm[16], m[16];
memset(rm, 0x00, 16*4);
rm[0]=cos(x);
rm[1]=sin(x);
rm[4]=-sin(x);
rm[5]=cos(x);
rm[10]=1.0f;
rm[15]=1.0f;
multMatrix44(tm,rm,m);
memcpy(tm,m,16*sizeof(float));
}
void scaleMatrix(float* tm, float x, float y, float z)
{
tm[0]*=x; tm[4]*=x; tm[8]*=x; tm[12]*=x;
tm[1]*=y; tm[5]*=y; tm[9]*=y; tm[13]*=y;
tm[2]*=z; tm[6]*=z; tm[10]*=z; tm[14]*=z;
}
void initProjectionMatrix(float* m, float fovy, float aspect, float near, float far)
{
float top = near*tan(fovy/2);
float right = (top*aspect);
*(m++) = near/right;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = near/top;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = 0.0f;
// *(m++) = -(far+near)/(far-near);
*(m++) = 0.0f;
// *(m++) = -2.0f*(far*near)/(far-near);
// *(m++) = 1.0f;
*(m++) = -1.0f;
*(m++) = 0.0f;
*(m++) = 0.0f;
*(m++) = -1.0f;
*(m++) = 0.0f;
}
void setUniformMatrix(u32 startreg, float* m)
{
float param[16];
param[0x0]=m[3]; //w
param[0x1]=m[2]; //z
param[0x2]=m[1]; //y
param[0x3]=m[0]; //x
param[0x4]=m[7];
param[0x5]=m[6];
param[0x6]=m[5];
param[0x7]=m[4];
param[0x8]=m[11];
param[0x9]=m[10];
param[0xa]=m[9];
param[0xb]=m[8];
param[0xc]=m[15];
param[0xd]=m[14];
param[0xe]=m[13];
param[0xf]=m[12];
GPU_SetUniform(startreg, (u32*)param, 4);
}
float angle=0.0f;
float angleZ=0.0f;
float tx, ty, tz;
typedef enum{
GPU_TRIANGLES = 0x0000,
GPU_TRIANGLE_STRIP = 0x0100,
GPU_TRIANGLE_FAN = 0x0200,
GPU_UNKPRIM = 0x0300 // ?
}GPU_Primitive_t;
void GPU_DrawArray(GPU_Primitive_t primitive, u32 n)
{
// //?
// GPUCMD_AddSingleParam(0x00040080, 0x00010000);
//set primitive type
GPUCMD_AddSingleParam(0x0002025E, primitive);
GPUCMD_AddSingleParam(0x0002025F, 0x00000001);
//index buffer not used for drawArrays but 0x000F0227 still required
GPUCMD_AddSingleParam(0x000F0227, 0x80000000);
//pass number of vertices
GPUCMD_AddSingleParam(0x000F0228, n);
GPUCMD_AddSingleParam(0x00010253, 0x00000001);
GPUCMD_AddSingleParam(0x00010245, 0x00000000);
GPUCMD_AddSingleParam(0x000F022E, 0x00000001);
GPUCMD_AddSingleParam(0x00010245, 0x00000001);
GPUCMD_AddSingleParam(0x000F0231, 0x00000001);
}
void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n)
{
//set primitive type
GPUCMD_AddSingleParam(0x0002025E, primitive);
GPUCMD_AddSingleParam(0x0002025F, 0x00000001);
//index buffer (TODO : support multiple types)
GPUCMD_AddSingleParam(0x000F0227, 0x80000000|((u32)indexArray));
//pass number of vertices
GPUCMD_AddSingleParam(0x000F0228, n);
GPUCMD_AddSingleParam(0x00020229, 0x00000100);
GPUCMD_AddSingleParam(0x00020253, 0x00000100);
GPUCMD_AddSingleParam(0x00010245, 0x00000000);
GPUCMD_AddSingleParam(0x000F022F, 0x00000001);
GPUCMD_AddSingleParam(0x00010245, 0x00000001);
GPUCMD_AddSingleParam(0x000F0231, 0x00000001);
}
static inline void* convertVAtoPA(void* a)
{
if((void*)a>(void*)0x1F000000)return (u32*)((void*)a-(void*)0x07000000);
return (u32*)((void*)a-(void*)gspHeap+(void*)0x20000000);
}
u32* gpuOut=(u32*)0x1F119400;
u32* gpuDOut=(u32*)0x1F370800;
void SHDR_UseProgram_(DVLB_s* dvlb, u8 id)
{
if(!dvlb || id>dvlb->numDVLE)return;
DVLE_s* dvle=&dvlb->DVLE[id];
//?
GPUCMD_AddSingleParam(0x00010229, 0x00000000);
GPUCMD_AddSingleParam(0x00010244, 0x00000000);
DVLP_SendCode(&dvlb->DVLP);
DVLP_SendOpDesc(&dvlb->DVLP);
DVLE_SendConstants(dvle);
GPUCMD_AddSingleParam(0x00080229, 0x00000000);
GPUCMD_AddSingleParam(0x000F02BA, 0x7FFF0000|(dvle->mainOffset&0xFFFF)); //set entrypoint
// should all be part of DVLE_SendOutmap ?
GPUCMD_AddSingleParam(0x000F0252, 0x00000000);
DVLE_SendOutmap(dvle);
//?
GPUCMD_AddSingleParam(0x000F0064, 0x00000001);
GPUCMD_AddSingleParam(0x000F006F, 0x00000703);
}
// topscreen
void doFrame1()
{
static u32 param[0x400];
static u32 zero[0x400];
memset(zero, 0x00, 0x400*4);
//general setup
GPU_SetViewport(convertVAtoPA(gpuDOut),convertVAtoPA(gpuOut),0,0,240*2,400);
GPU_DepthRange(-1.0f, 0.0f);
GPU_SetFaceCulling(GPU_CULL_BACK_CCW);
GPU_SetStencilTest(false, GPU_ALWAYS, 0x00);
GPU_SetDepthTest(true, GPU_GREATER, 0x1F);
// ?
GPUCMD_AddSingleParam(0x00010062, 0x00000000); //param always 0x0 according to code
GPUCMD_AddSingleParam(0x000F0118, 0x00000000);
//setup shader
SHDR_UseProgram_(shader, 0);
//attribute buffers
GPU_SetAttributeBuffers(3, convertVAtoPA(vertArray),
GPU_ATTRIBFMT(0, 3, GPU_FLOAT)|GPU_ATTRIBFMT(1, 2, GPU_FLOAT)|GPU_ATTRIBFMT(2, 3, GPU_FLOAT),
0xFFC, 0x210, 1, (u32[]){0x00000000}, (u64[]){0x210}, (u8[]){3});
//?
GPUCMD_AddSingleParam(0x000F0100, 0x00E40100);
GPUCMD_AddSingleParam(0x000F0101, 0x01010000);
GPUCMD_AddSingleParam(0x000F0104, 0x00000010);
//texturing stuff
GPUCMD_AddSingleParam(0x0002006F, 0x00000100);
GPUCMD_AddSingleParam(0x000F0080, 0x00011001); //enables/disables texturing
//texenv
GPU_SetTexEnv(3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00000000);
GPU_SetTexEnv(4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00000000);
GPU_SetTexEnv(5, GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
GPU_TEVOPERANDS(0,0,0), GPU_TEVOPERANDS(0,0,0), GPU_MODULATE, GPU_MODULATE, 0xFFFFFFFF);
//texturing stuff
GPU_SetTexture(convertVAtoPA(texData),256,256,0x6,GPU_RGBA8);
//setup matrices
float modelView[16];
float projection[16];
loadIdentity44(modelView);
loadIdentity44(projection);
translateMatrix(modelView, tx, ty, tz);
rotateMatrixX(modelView, angle);
rotateMatrixZ(modelView, angleZ);
initProjectionMatrix(projection, 1.3962634f, 240.0f/400.0f, 0.01f, 10.0f);
setUniformMatrix(0x20, modelView);
// setUniformMatrix(0x24, projection);
setUniformMatrix(0x80, projection);
//draw first model
GPU_DrawArray(GPU_TRIANGLES, mdlFaces*3);
// GPU_DrawElements(GPU_TRIANGLES, (u32*)(((u32)((void*)indArray-(void*)gspHeap))+0x20000000-base), 6);
//setup matrices
loadIdentity44(modelView);
loadIdentity44(projection);
translateMatrix(modelView, tx, -ty, tz);
rotateMatrixX(modelView, -angle);
rotateMatrixZ(modelView, -angleZ);
setUniformMatrix(0x20, modelView);
//draw second
GPU_DrawArray(GPU_TRIANGLES, mdlFaces*3);
//finalize stuff ?
GPUCMD_AddSingleParam(0x000F0111, 0x00000001);
GPUCMD_AddSingleParam(0x000F0110, 0x00000001);
GPUCMD_AddSingleParam(0x0008025E, 0x00000000);
}
void doFrame3()
{
GPUCMD_AddSingleParam(0x0008025E, 0x00000000);
}
void doModel()
{
memcpy(vertArray, mdlData, sizeof(mdlData));
}
Result GX_RequestDma_(u32* gxbuf, u32* src, u32* dst, u32 length)
{
u32 gxCommand[0x8];
gxCommand[0]=0x00; //CommandID
gxCommand[1]=(u32)src; //source address
gxCommand[2]=(u32)dst; //destination address
gxCommand[3]=length; //size
gxCommand[4]=gxCommand[5]=gxCommand[6]=gxCommand[7]=0x0;
gxCommand[7]=0x01;
return GSPGPU_submitGxCommand(gxbuf, gxCommand, NULL);
}
extern u32* gpuCmdBuf;
extern u32 gpuCmdBufSize;
extern u32 gpuCmdBufOffset;
void GPUCMD_Run_(u32* gxbuf)
{
waitGSP();
GX_SetCommandList_First(gxbuf, gpuCmdBuf, gpuCmdBufOffset*4, NULL, 0, NULL, 0);
waitGSP();
GX_SetCommandList_Last(gxbuf, gpuCmdBuf, gpuCmdBufOffset*4, 0x0);
}
int main()
{
initSrv();
aptInit(APPID_APPLICATION);
gspGpuInit();
hidInit(NULL);
aptSetupEventHandler();
GPU_Init(NULL);
u32* gpuCmd=(u32*)(&gspHeap[0x200000]);
u32 gpuCmdSize=0x10000;
GPU_Reset(gxCmdBuf, gpuCmd, gpuCmdSize);
vertArray=(float*)&gpuCmd[gpuCmdSize];
colorArray=(float*)&vertArray[0x300];
indArray=(u16*)&colorArray[0x100];
texData=(u32*)&indArray[0x10000];
memset(vertArray, 0x00, 0x500*4);
// memset(texData, 0xFF, 32*32*4);
int i;
// for(i=0;i<32*32;i++)texData[i]=0xFFFF00FF; //RGBA, in that order
// for(i=0;i<32*32;i++)texData[i]=(0xFF<<24)|((((i&0x1f)*8)&0xFF)<<16)|(0x00<<8)|(0xFF); //RGBA, in that order
// for(i=0;i<32*32;i++)texData[i]=(0xFF<<24)|(i<<8)|(0xFF); //RGBA, in that order
// for(i=0;i<240*400;i++)texData[i]=(0xFF<<24)|(i<<8)|(0xFF); //RGBA, in that order
memcpy(texData, test_png_bin, test_png_bin_size);
//VA init
// //triangle 0
// vertArray[0*4+0]=0.25f;
// vertArray[0*4+1]=0.0f/2;
// vertArray[0*4+2]=0.0f;
// vertArray[0*4+3]=1.0f;
// vertArray[1*4+0]=0.0f;
// vertArray[1*4+1]=-0.25f/2;
// vertArray[1*4+2]=0.0f;
// vertArray[1*4+3]=1.0f;
// vertArray[2*4+0]=0.0f;
// vertArray[2*4+1]=0.25f/2;
// vertArray[2*4+2]=0.0f;
// vertArray[2*4+3]=1.0f;
// //triangle 1
// vertArray[3*4+0]=0.0f;
// vertArray[3*4+1]=-0.25f/2;
// vertArray[3*4+2]=0.0f;
// vertArray[3*4+3]=1.0f;
// vertArray[4*4+0]=-0.25f;
// vertArray[4*4+1]=-0.5f/2;
// vertArray[4*4+2]=0.0f;
// vertArray[4*4+3]=1.0f;
// vertArray[5*4+0]=-0.25f;
// vertArray[5*4+1]=0.0f/2;
// vertArray[5*4+2]=0.0f;
// vertArray[5*4+3]=1.0f;
// //triangle 2
// vertArray[6*4+0]=0.0f;
// vertArray[6*4+1]=0.25f/2;
// vertArray[6*4+2]=0.0f;
// vertArray[6*4+3]=1.0f;
// vertArray[7*4+0]=-0.25f;
// vertArray[7*4+1]=0.0f/2;
// vertArray[7*4+2]=0.0f;
// vertArray[7*4+3]=1.0f;
// vertArray[8*4+0]=-0.25f;
// vertArray[8*4+1]=0.5f/2;
// vertArray[8*4+2]=0.0f;
// vertArray[8*4+3]=1.0f;
const int stride=5;
//triangle 0
vertArray[0*stride+0]=0.0f;
vertArray[0*stride+1]=0.0f;
vertArray[0*stride+2]=0.0f;
vertArray[0*stride+3]=0.0f;
vertArray[0*stride+4]=0.0f;
vertArray[1*stride+0]=1.0f;
vertArray[1*stride+1]=0.0f;
vertArray[1*stride+2]=0.0f;
vertArray[1*stride+3]=1.0f;
vertArray[1*stride+4]=0.0f;
vertArray[2*stride+0]=1.0f;
vertArray[2*stride+1]=1.0f;
vertArray[2*stride+2]=0.0f;
vertArray[2*stride+3]=1.0f;
vertArray[2*stride+4]=1.0f;
//triangle 1
vertArray[3*stride+0]=0.0f;
vertArray[3*stride+1]=1.0f;
vertArray[3*stride+2]=0.0f;
vertArray[3*stride+3]=0.0f;
vertArray[3*stride+4]=1.0f;
vertArray[4*stride+0]=0.0f;
vertArray[4*stride+1]=0.0f;
vertArray[4*stride+2]=0.0f;
vertArray[4*stride+3]=0.0f;
vertArray[4*stride+4]=0.0f;
vertArray[5*stride+0]=1.0f;
vertArray[5*stride+1]=1.0f;
vertArray[5*stride+2]=0.0f;
vertArray[5*stride+3]=1.0f;
vertArray[5*stride+4]=1.0f;
//IA init (unused atm)
indArray[0]=0;
indArray[1]=1;
indArray[2]=2;
indArray[3]=3;
indArray[4]=4;
indArray[5]=5;
doModel();
tx=ty=0.0f;
tz=-0.1f;
shader=SHDR_ParseSHBIN((u32*)test_shbin,test_shbin_size);
waitGSP();
GX_SetMemoryFill(gxCmdBuf, (u32*)gpuOut, 0x404040FF, (u32*)&gpuOut[0x2EE00], 0x201, (u32*)gpuDOut, 0x00000000, (u32*)&gpuDOut[0x2EE00], 0x201);
APP_STATUS status;
while((status=aptGetStatus())!=APP_EXITING)
{
if(status==APP_RUNNING)
{
u32 PAD=hidSharedMem[7];
u32 regData=PAD|0x01000000;
if(!PAD)regData=0x0;
GSPGPU_WriteHWRegs(NULL, 0x202A04, &regData, 4);
if(PAD&PAD_SELECT)
{
if(PAD&PAD_R)
{
if(PAD&PAD_UP)vertArray[0*stride+0]+=0.1f;
if(PAD&PAD_DOWN)vertArray[0*stride+0]-=0.1f;
if(PAD&PAD_LEFT)vertArray[0*stride+1]+=0.1f;
if(PAD&PAD_RIGHT)vertArray[0*stride+1]-=0.1f;
if(PAD&PAD_A)vertArray[0*stride+2]+=0.1f;
if(PAD&PAD_Y)vertArray[0*stride+2]-=0.1f;
if(PAD&PAD_X)vertArray[0*stride+3]+=0.1f;
if(PAD&PAD_B)vertArray[0*stride+3]-=0.1f;
}else if(PAD&PAD_L)
{
if(PAD&PAD_UP)vertArray[1*stride+0]+=0.1f;
if(PAD&PAD_DOWN)vertArray[1*stride+0]-=0.1f;
if(PAD&PAD_LEFT)vertArray[1*stride+1]+=0.1f;
if(PAD&PAD_RIGHT)vertArray[1*stride+1]-=0.1f;
if(PAD&PAD_A)vertArray[1*stride+2]+=0.1f;
if(PAD&PAD_Y)vertArray[1*stride+2]-=0.1f;
if(PAD&PAD_X)vertArray[1*stride+3]+=0.1f;
if(PAD&PAD_B)vertArray[1*stride+3]-=0.1f;
}else{
if(PAD&PAD_UP)vertArray[2*stride+0]+=0.1f;
if(PAD&PAD_DOWN)vertArray[2*stride+0]-=0.1f;
if(PAD&PAD_LEFT)vertArray[2*stride+1]+=0.1f;
if(PAD&PAD_RIGHT)vertArray[2*stride+1]-=0.1f;
if(PAD&PAD_A)vertArray[2*stride+2]+=0.1f;
if(PAD&PAD_Y)vertArray[2*stride+2]-=0.1f;
if(PAD&PAD_X)vertArray[2*stride+3]+=0.1f;
if(PAD&PAD_B)vertArray[2*stride+3]-=0.1f;
}
}else{
if(PAD&PAD_UP)tx+=0.1f;
if(PAD&PAD_DOWN)tx-=0.1f;
if(PAD&PAD_LEFT)ty+=0.1f;
if(PAD&PAD_RIGHT)ty-=0.1f;
if(PAD&PAD_R)tz+=0.1f;
if(PAD&PAD_L)tz-=0.1f;
if(PAD&PAD_A)angle+=0.1f;
if(PAD&PAD_Y)angle-=0.1f;
if(PAD&PAD_X)angleZ+=0.1f;
if(PAD&PAD_B)angleZ-=0.1f;
}
waitGSP();
GX_SetDisplayTransfer(gxCmdBuf, (u32*)gpuOut, 0x019001E0, (u32*)topLeftFramebuffers[currentBuffer^1], 0x019001E0, 0x01001000);
waitGSP();
GX_SetMemoryFill(gxCmdBuf, (u32*)gpuOut, 0x404040FF, (u32*)&gpuOut[0x2EE00], 0x201, (u32*)gpuDOut, 0x00000000, (u32*)&gpuDOut[0x2EE00], 0x201);
svc_sleepThread(1000000); //not sure how to do proper GPU (v)sync yet
// GPUCMD_SetBuffer((u32*)gspHeap, gpuCmdSize, 0);
// GPUCMD_AddSingleParam(0x0008025E, 0x00000000);
// GPUCMD_Finalize();
// GPUCMD_Run(gxCmdBuf);
GPUCMD_SetBuffer(gpuCmd, gpuCmdSize, 0);
doFrame1();
GPUCMD_Finalize();
GPUCMD_Run_(gxCmdBuf);
swapBuffers();
}
svc_sleepThread(16666666/2);
}
hidExit();
gspGpuExit();
aptExit();
svc_exitProcess();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment