libctru/libctru/source/gfx.c

304 lines
8.2 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <3ds/types.h>
#include <3ds/gfx.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/allocator/linear.h>
#include <3ds/allocator/mappable.h>
#include <3ds/allocator/vram.h>
#include <3ds/gpu/gx.h>
GSPGPU_FramebufferInfo topFramebufferInfo, bottomFramebufferInfo;
u8 gfxThreadID;
u8* gfxSharedMemory;
u8* gfxTopLeftFramebuffers[2];
u8* gfxTopRightFramebuffers[2];
u8* gfxBottomFramebuffers[2];
u32 gfxTopFramebufferMaxSize;
u32 gfxBottomFramebufferMaxSize;
static bool enable3d;
static u8 currentBuffer[2];
static int doubleBuf[2] = {1,1};
Handle gspEvent, gspSharedMemHandle;
static GSPGPU_FramebufferFormats topFormat = GSP_BGR8_OES;
static GSPGPU_FramebufferFormats botFormat = GSP_BGR8_OES;
static GSPGPU_FramebufferInfo* const framebufferInfoSt[] = { &topFramebufferInfo, &bottomFramebufferInfo };
static void (*screenFree)(void *) = NULL;
static void *(*screenAlloc)(size_t) = NULL;
void gfxSet3D(bool enable)
{
enable3d=enable;
}
bool gfxIs3D(void)
{
return enable3d;
}
u32 __get_bytes_per_pixel(GSPGPU_FramebufferFormats format) {
switch(format) {
case GSP_RGBA8_OES:
return 4;
case GSP_BGR8_OES:
return 3;
case GSP_RGB565_OES:
case GSP_RGB5_A1_OES:
case GSP_RGBA4_OES:
return 2;
}
return 3;
}
void gfxSetScreenFormat(gfxScreen_t screen, GSPGPU_FramebufferFormats format) {
if (screenAlloc == NULL || screenFree == NULL) return;
if(screen==GFX_TOP)
{
u32 topSize = 400 * 240 * __get_bytes_per_pixel(format);
if (gfxTopFramebufferMaxSize < topSize)
{
screenFree(gfxTopLeftFramebuffers[0]);
screenFree(gfxTopLeftFramebuffers[1]);
screenFree(gfxTopRightFramebuffers[0]);
screenFree(gfxTopRightFramebuffers[1]);
gfxTopLeftFramebuffers[0]=screenAlloc(topSize);
gfxTopLeftFramebuffers[1]=screenAlloc(topSize);
gfxTopRightFramebuffers[0]=screenAlloc(topSize);
gfxTopRightFramebuffers[1]=screenAlloc(topSize);
gfxTopFramebufferMaxSize = topSize;
}
topFormat = format;
}else{
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(format);
if (gfxBottomFramebufferMaxSize < bottomSize)
{
screenFree(gfxBottomFramebuffers[0]);
screenFree(gfxBottomFramebuffers[1]);
gfxBottomFramebuffers[0]=screenAlloc(bottomSize);
gfxBottomFramebuffers[1]=screenAlloc(bottomSize);
gfxBottomFramebufferMaxSize = bottomSize;
}
botFormat = format;
}
}
GSPGPU_FramebufferFormats gfxGetScreenFormat(gfxScreen_t screen) {
if(screen==GFX_TOP)
return topFormat;
else
return botFormat;
}
void gfxSetDoubleBuffering(gfxScreen_t screen, bool doubleBuffering) {
doubleBuf[screen] = doubleBuffering ? 1 : 0; // make sure they're the integer values '1' and '0'
}
void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id)
{
if(screen==GFX_TOP)
{
topFramebufferInfo.active_framebuf=id;
topFramebufferInfo.framebuf0_vaddr=(u32*)gfxTopLeftFramebuffers[id];
if(enable3d)topFramebufferInfo.framebuf1_vaddr=(u32*)gfxTopRightFramebuffers[id];
else topFramebufferInfo.framebuf1_vaddr=topFramebufferInfo.framebuf0_vaddr;
topFramebufferInfo.framebuf_widthbytesize=240*__get_bytes_per_pixel(topFormat);
u8 bit5=(enable3d!=0);
topFramebufferInfo.format=((1)<<8)|((1^bit5)<<6)|((bit5)<<5)|topFormat;
topFramebufferInfo.framebuf_dispselect=id;
topFramebufferInfo.unk=0x00000000;
}else{
bottomFramebufferInfo.active_framebuf=id;
bottomFramebufferInfo.framebuf0_vaddr=(u32*)gfxBottomFramebuffers[id];
bottomFramebufferInfo.framebuf1_vaddr=0x00000000;
bottomFramebufferInfo.framebuf_widthbytesize=240*__get_bytes_per_pixel(botFormat);
bottomFramebufferInfo.format=botFormat;
bottomFramebufferInfo.framebuf_dispselect=id;
bottomFramebufferInfo.unk=0x00000000;
}
}
void gfxWriteFramebufferInfo(gfxScreen_t screen)
{
s32* framebufferInfoHeader=(s32*)(gfxSharedMemory+0x200+gfxThreadID*0x80);
if(screen==GFX_BOTTOM)framebufferInfoHeader+=0x10;
GSPGPU_FramebufferInfo* framebufferInfo=(GSPGPU_FramebufferInfo*)&framebufferInfoHeader[1];
union
{
s32 header;
struct { u8 swap, update; };
} info;
info.header = __ldrex(framebufferInfoHeader);
info.swap = !info.swap;
u8 pos = info.swap;
info.update = 1;
framebufferInfo[pos]=*framebufferInfoSt[screen];
while (__strex(framebufferInfoHeader,info.header))
{
info.header = __ldrex(framebufferInfoHeader);
info.swap = pos;
info.update = 1;
}
}
void gfxInit(GSPGPU_FramebufferFormats topFormat, GSPGPU_FramebufferFormats bottomFormat, bool vrambuffers)
{
if (vrambuffers)
{
screenAlloc=vramAlloc;
screenFree=vramFree;
} else {
screenAlloc=linearAlloc;
screenFree=linearFree;
}
gspInit();
gfxSharedMemory=(u8*)mappableAlloc(0x1000);
GSPGPU_AcquireRight(0x0);
//setup our gsp shared mem section
svcCreateEvent(&gspEvent, RESET_ONESHOT);
GSPGPU_RegisterInterruptRelayQueue(gspEvent, 0x1, &gspSharedMemHandle, &gfxThreadID);
svcMapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory, 0x3, 0x10000000);
// default gspHeap configuration :
// topleft1 0x00000000-0x00046500
// topleft2 0x00046500-0x0008CA00
// bottom1 0x0008CA00-0x000C4E00
// bottom2 0x000C4E00-0x000FD200
// if 3d enabled :
// topright1 0x000FD200-0x00143700
// topright2 0x00143700-0x00189C00
u32 topSize = 400 * 240 * __get_bytes_per_pixel(topFormat);
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(bottomFormat);
gfxTopLeftFramebuffers[0]=screenAlloc(topSize);
gfxTopLeftFramebuffers[1]=screenAlloc(topSize);
gfxBottomFramebuffers[0]=screenAlloc(bottomSize);
gfxBottomFramebuffers[1]=screenAlloc(bottomSize);
gfxTopRightFramebuffers[0]=screenAlloc(topSize);
gfxTopRightFramebuffers[1]=screenAlloc(topSize);
gfxTopFramebufferMaxSize = topSize;
gfxBottomFramebufferMaxSize = bottomSize;
enable3d=false;
//set requested modes
gfxSetScreenFormat(GFX_TOP,topFormat);
gfxSetScreenFormat(GFX_BOTTOM,bottomFormat);
//initialize framebuffer info structures
gfxSetFramebufferInfo(GFX_TOP, 0);
gfxSetFramebufferInfo(GFX_BOTTOM, 0);
//GSP shared mem : 0x2779F000
gxCmdBuf=(u32*)(gfxSharedMemory+0x800+gfxThreadID*0x200);
currentBuffer[0]=0;
currentBuffer[1]=0;
// Initialize event handler and wait for VBlank
gspInitEventHandler(gspEvent, (vu8*) gfxSharedMemory, gfxThreadID);
gspWaitForVBlank();
GSPGPU_SetLcdForceBlack(0x0);
}
void gfxInitDefault(void) {
gfxInit(GSP_BGR8_OES,GSP_BGR8_OES,false);
}
void gfxExit(void)
{
if (screenFree == NULL) return;
// Exit event handler
gspExitEventHandler();
// Free framebuffers
screenFree(gfxTopRightFramebuffers[1]);
screenFree(gfxTopRightFramebuffers[0]);
screenFree(gfxBottomFramebuffers[1]);
screenFree(gfxBottomFramebuffers[0]);
screenFree(gfxTopLeftFramebuffers[1]);
screenFree(gfxTopLeftFramebuffers[0]);
//unmap GSP shared mem
svcUnmapMemoryBlock(gspSharedMemHandle, (u32)gfxSharedMemory);
GSPGPU_UnregisterInterruptRelayQueue();
svcCloseHandle(gspSharedMemHandle);
if(gfxSharedMemory != NULL)
{
mappableFree(gfxSharedMemory);
gfxSharedMemory = NULL;
}
svcCloseHandle(gspEvent);
GSPGPU_ReleaseRight();
gspExit();
screenFree = NULL;
}
u8* gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16* width, u16* height)
{
if(width)*width=240;
if(screen==GFX_TOP)
{
if(height)*height=400;
return (side==GFX_LEFT || !enable3d)?(gfxTopLeftFramebuffers[currentBuffer[0]^doubleBuf[0]]):(gfxTopRightFramebuffers[currentBuffer[0]^doubleBuf[0]]);
}else{
if(height)*height=320;
return gfxBottomFramebuffers[currentBuffer[1]^doubleBuf[1]];
}
}
void gfxFlushBuffers(void)
{
u32 topSize = 400 * 240 * __get_bytes_per_pixel(gfxGetScreenFormat(GFX_TOP));
u32 bottomSize = 320 * 240 * __get_bytes_per_pixel(gfxGetScreenFormat(GFX_BOTTOM));
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), topSize);
if(enable3d)GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL), topSize);
GSPGPU_FlushDataCache(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), bottomSize);
}
void gfxConfigScreen(gfxScreen_t scr, bool immediate)
{
currentBuffer[scr]^=doubleBuf[scr];
gfxSetFramebufferInfo(scr, currentBuffer[scr]);
if (immediate)
GSPGPU_SetBufferSwap(scr, framebufferInfoSt[scr]);
else
gfxWriteFramebufferInfo(scr);
}
void gfxSwapBuffers(void)
{
gfxConfigScreen(GFX_TOP, true);
gfxConfigScreen(GFX_BOTTOM, true);
}
void gfxSwapBuffersGpu(void)
{
gfxConfigScreen(GFX_TOP, false);
gfxConfigScreen(GFX_BOTTOM, false);
}