Compare commits

..

No commits in common. "master" and "v1.5.0" have entirely different histories.

14 changed files with 166 additions and 138 deletions

View File

@ -9,8 +9,8 @@ endif
include $(DEVKITARM)/3ds_rules
export CITRO3D_MAJOR := 1
export CITRO3D_MINOR := 7
export CITRO3D_PATCH := 1
export CITRO3D_MINOR := 5
export CITRO3D_PATCH := 0
VERSION := $(CITRO3D_MAJOR).$(CITRO3D_MINOR).$(CITRO3D_PATCH)
@ -31,11 +31,11 @@ INCLUDES := include
#---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -g -Wall -Wno-sizeof-array-div -Werror -mword-relocations \
CFLAGS := -g -Wall -Werror -mword-relocations \
-ffunction-sections -fdata-sections \
$(ARCH) $(BUILD_CFLAGS)
CFLAGS += $(INCLUDE) -D__3DS__ -DCITRO3D_BUILD
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCITRO3D_BUILD
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
@ -114,7 +114,7 @@ debug:
lib/libcitro3d.a : lib release $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2 -fomit-frame-pointer -fno-math-errno" \
BUILD_CFLAGS="-DNDEBUG=1 -O2 -fomit-frame-pointer" \
DEPSDIR=$(CURDIR)/release \
--no-print-directory -C release \
-f $(CURDIR)/Makefile

View File

@ -11,6 +11,7 @@ enum
};
bool C3D_Init(size_t cmdBufSize);
void C3D_FlushAsync(void);
void C3D_Fini(void);
float C3D_GetCmdBufUsage(void);
@ -33,6 +34,23 @@ static inline void C3D_ImmDrawRestartPrim(void)
GPUCMD_AddWrite(GPUREG_RESTART_PRIMITIVE, 1);
}
static inline void C3D_FlushAwait(void)
{
gspWaitForP3D();
}
static inline void C3D_Flush(void)
{
C3D_FlushAsync();
C3D_FlushAwait();
}
static inline void C3D_VideoSync(void)
{
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
gfxSwapBuffersGpu();
}
// Fixed vertex attributes
C3D_FVec* C3D_FixedAttribGetWritePtr(int id);

View File

@ -77,14 +77,6 @@ enum
void C3D_LightEnvFresnel(C3D_LightEnv* env, GPU_FRESNELSEL selector);
void C3D_LightEnvBumpMode(C3D_LightEnv* env, GPU_BUMPMODE mode);
void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit);
/**
* @brief Configures whether to use the z component of the normal map.
* @param[out] env Pointer to light environment structure.
* @param[in] enable false if the z component is reconstructed from the xy components
* of the normal map, true if the z component is taken from the normal map.
*/
void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool enable);
void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode);
void C3D_LightEnvShadowSel(C3D_LightEnv* env, int texUnit);
void C3D_LightEnvClampHighlights(C3D_LightEnv* env, bool clamp);

View File

@ -13,12 +13,7 @@
* The one true circumference-to-radius ratio.
* See http://tauday.com/tau-manifesto
*/
#define M_TAU (6.28318530717958647692528676655900576)
// Define the legacy circle constant as well
#ifndef M_PI
#define M_PI (M_TAU/2)
#endif
#define M_TAU (2*M_PI)
/**
* @brief Convert an angle from revolutions to radians

View File

@ -64,11 +64,6 @@ C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face,
void C3D_RenderTargetDelete(C3D_RenderTarget* target);
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);

View File

@ -41,7 +41,7 @@ typedef struct
};
} C3D_Tex;
typedef struct CTR_ALIGN(8)
typedef struct ALIGN(8)
{
u16 width;
u16 height;

View File

@ -1,5 +1,5 @@
#pragma once
#if defined(__3DS__) || defined(_3DS)
#ifdef _3DS
#include <3ds.h>
#else
#include <stdbool.h>

View File

@ -38,7 +38,7 @@ extern "C" {
#endif
/** @brief Subtexture
* @note If top < bottom, the subtexture is rotated 1/4 revolution counter-clockwise
* @note If top > bottom, the subtexture is rotated 1/4 revolution counter-clockwise
*/
typedef struct Tex3DS_SubTexture
{

View File

@ -8,6 +8,14 @@ C3D_Context __C3D_Context;
static aptHookCookie hookCookie;
__attribute__((weak)) void C3Di_RenderQueueWaitDone(void)
{
}
__attribute__((weak)) void C3Di_RenderQueueExit(void)
{
}
__attribute__((weak)) void C3Di_LightEnvUpdate(C3D_LightEnv* env)
{
(void)env;
@ -42,12 +50,10 @@ static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param)
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;
@ -96,6 +102,10 @@ bool C3D_Init(size_t cmdBufSize)
return false;
}
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
GX_BindQueue(&ctx->gxQueue);
gxCmdQueueRun(&ctx->gxQueue);
ctx->flags = C3DiF_Active | C3DiF_TexEnvBuf | C3DiF_TexEnvAll | C3DiF_Effect | C3DiF_TexStatus | C3DiF_TexAll;
// TODO: replace with direct struct access
@ -127,7 +137,6 @@ bool C3D_Init(size_t cmdBufSize)
ctx->fixedAttribDirty = 0;
ctx->fixedAttribEverDirty = 0;
C3Di_RenderQueueInit();
aptHook(&hookCookie, C3Di_AptEventHook, NULL);
return true;
@ -327,6 +336,25 @@ bool C3Di_SplitFrame(u32** pBuf, u32* pSize)
return true;
}
void C3D_FlushAsync(void)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return;
u32* cmdBuf;
u32 cmdBufSize;
C3Di_SplitFrame(&cmdBuf, &cmdBufSize);
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
//take advantage of GX_FlushCacheRegions to flush gsp heap
extern u32 __ctru_linear_heap;
extern u32 __ctru_linear_heap_size;
GX_FlushCacheRegions(cmdBuf, cmdBufSize*4, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0);
GX_ProcessCommandList(cmdBuf, cmdBufSize*4, 0x0);
}
float C3D_GetCmdBufUsage(void)
{
return C3Di_GetContext()->cmdBufUsage;
@ -339,8 +367,11 @@ void C3D_Fini(void)
if (!(ctx->flags & C3DiF_Active))
return;
aptUnhook(&hookCookie);
C3Di_RenderQueueExit();
aptUnhook(&hookCookie);
gxCmdQueueStop(&ctx->gxQueue);
gxCmdQueueWait(&ctx->gxQueue, -1);
GX_BindQueue(NULL);
free(ctx->gxQueue.entries);
linearFree(ctx->cmdBuf);
ctx->flags = 0;

View File

@ -124,18 +124,6 @@ static inline bool C3Di_TexIs2D(C3D_Tex* tex)
return !typeIsCube(C3D_TexGetType(tex));
}
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;
}
void C3Di_UpdateContext(void);
void C3Di_AttrInfoBind(C3D_AttrInfo* info);
void C3Di_BufInfoBind(C3D_BufInfo* info);
@ -152,9 +140,3 @@ void C3Di_LoadShaderUniforms(shaderInstance_s* si);
void C3Di_ClearShaderUniforms(GPU_SHADER_TYPE type);
bool C3Di_SplitFrame(u32** pBuf, u32* pSize);
void C3Di_RenderQueueInit(void);
void C3Di_RenderQueueExit(void);
void C3Di_RenderQueueWaitDone(void);
void C3Di_RenderQueueEnableVBlank(void);
void C3Di_RenderQueueDisableVBlank(void);

View File

@ -250,14 +250,6 @@ void C3D_LightEnvBumpSel(C3D_LightEnv* env, int texUnit)
env->flags |= C3DF_LightEnv_Dirty;
}
void C3D_LightEnvBumpNormalZ(C3D_LightEnv *env, bool usez) {
if (usez)
env->conf.config[0] |= BIT(30);
else
env->conf.config[0] &= ~BIT(30);
env->flags |= C3DF_LightEnv_Dirty;
}
void C3D_LightEnvShadowMode(C3D_LightEnv* env, u32 mode)
{
mode &= 0xF<<16;

View File

@ -8,16 +8,22 @@ static C3D_RenderTarget *linkedTarget[3];
static TickCounter gpuTime, cpuTime;
#define STAGE_HAS_TRANSFER(n) BIT(0+(n))
#define STAGE_HAS_ANY_TRANSFER (7<<0)
#define STAGE_NEED_TRANSFER(n) BIT(3+(n))
#define STAGE_NEED_TOP_TRANSFER (STAGE_NEED_TRANSFER(0)|STAGE_NEED_TRANSFER(1))
#define STAGE_NEED_BOT_TRANSFER STAGE_NEED_TRANSFER(2)
#define STAGE_WAIT_TRANSFER BIT(6)
static bool initialized;
static bool inFrame, inSafeTransfer, measureGpuTime;
static bool needSwapTop, needSwapBot, isTopStereo;
static u8 frameStage;
static float framerate = 60.0f;
static float framerateCounter[2] = { 60.0f, 60.0f };
static u32 frameCounter[2];
static void (* frameEndCb)(void*);
static void* frameEndCbData;
static void C3Di_RenderTargetDestroy(C3D_RenderTarget* target);
static bool framerateLimit(int id)
{
framerateCounter[id] -= framerate;
@ -31,12 +37,44 @@ static bool framerateLimit(int id)
static void onVBlank0(C3D_UNUSED void* unused)
{
if (frameStage & STAGE_NEED_TOP_TRANSFER)
{
C3D_RenderTarget *left = linkedTarget[0], *right = linkedTarget[1];
if (left && !(frameStage&STAGE_NEED_TRANSFER(0)))
left = NULL;
if (right && !(frameStage&STAGE_NEED_TRANSFER(1)))
right = NULL;
if (gfxIs3D() && !right)
right = left;
frameStage &= ~STAGE_NEED_TOP_TRANSFER;
if (left || right)
{
frameStage |= STAGE_WAIT_TRANSFER;
if (left)
C3D_FrameBufTransfer(&left->frameBuf, GFX_TOP, GFX_LEFT, left->transferFlags);
if (right)
C3D_FrameBufTransfer(&right->frameBuf, GFX_TOP, GFX_RIGHT, right->transferFlags);
gfxConfigScreen(GFX_TOP, false);
}
}
if (framerateLimit(0))
frameCounter[0]++;
}
static void onVBlank1(C3D_UNUSED void* unused)
{
if (frameStage & STAGE_NEED_BOT_TRANSFER)
{
frameStage &= ~STAGE_NEED_BOT_TRANSFER;
C3D_RenderTarget* target = linkedTarget[2];
if (target)
{
frameStage |= STAGE_WAIT_TRANSFER;
C3D_FrameBufTransfer(&target->frameBuf, GFX_BOTTOM, GFX_LEFT, target->transferFlags);
gfxConfigScreen(GFX_BOTTOM, false);
}
}
if (framerateLimit(1))
frameCounter[1]++;
}
@ -57,18 +95,12 @@ static void onQueueFinish(gxCmdQueue_s* queue)
gxCmdQueueClear(queue);
}
}
else if (frameStage & STAGE_WAIT_TRANSFER)
frameStage &= ~STAGE_WAIT_TRANSFER;
else
{
if (needSwapTop)
{
gfxScreenSwapBuffers(GFX_TOP, isTopStereo);
needSwapTop = false;
}
if (needSwapBot)
{
gfxScreenSwapBuffers(GFX_BOTTOM, false);
needSwapBot = false;
}
u8 needs = frameStage & STAGE_HAS_ANY_TRANSFER;
frameStage = (frameStage&~STAGE_HAS_ANY_TRANSFER) | (needs<<3);
}
}
@ -94,60 +126,72 @@ static bool C3Di_WaitAndClearQueue(s64 timeout)
gxCmdQueue_s* queue = &C3Di_GetContext()->gxQueue;
if (!gxCmdQueueWait(queue, timeout))
return false;
if (timeout==0 && frameStage)
return false;
while (frameStage)
gspWaitForAnyEvent();
gxCmdQueueStop(queue);
gxCmdQueueClear(queue);
return true;
}
void C3Di_RenderQueueEnableVBlank(void)
static void C3Di_RenderQueueInit(void)
{
gspSetEventCallback(GSPGPU_EVENT_VBlank0, onVBlank0, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, onVBlank1, NULL, false);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, onQueueFinish, NULL);
}
void C3Di_RenderQueueDisableVBlank(void)
{
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
}
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_RenderTargetDestroy(C3D_RenderTarget* target);
void C3Di_RenderQueueExit(void)
{
int i;
C3D_RenderTarget *a, *next;
if (!initialized)
return;
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);
}
gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, false);
gspSetEventCallback(GSPGPU_EVENT_VBlank1, NULL, NULL, false);
gxCmdQueueSetCallback(&C3Di_GetContext()->gxQueue, NULL, NULL);
for (i = 0; i < 3; i ++)
linkedTarget[i] = NULL;
initialized = false;
}
void C3Di_RenderQueueWaitDone(void)
{
if (!initialized)
return;
C3Di_WaitAndClearQueue(-1);
}
static bool checkRenderQueueInit(void)
{
C3D_Context* ctx = C3Di_GetContext();
if (!(ctx->flags & C3DiF_Active))
return false;
if (!initialized)
{
C3Di_RenderQueueInit();
initialized = true;
}
return true;
}
float C3D_FrameRate(float fps)
{
float old = framerate;
@ -162,17 +206,13 @@ float C3D_FrameRate(float fps)
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;
osTickCounterStart(&cpuTime);
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
return true;
}
@ -197,15 +237,13 @@ void C3D_FrameSplit(u8 flags)
void C3D_FrameEnd(u8 flags)
{
C3D_Context* ctx = C3Di_GetContext();
if (!inFrame) return;
if (frameEndCb)
frameEndCb(frameEndCbData);
C3D_FrameSplit(flags);
GPUCMD_SetBuffer(NULL, 0, 0);
osTickCounterUpdate(&cpuTime);
inFrame = false;
osTickCounterUpdate(&cpuTime);
// Flush the entire linear memory if the user did not explicitly mandate to flush the command list
if (!(flags & GX_CMDLIST_FLUSH))
@ -217,24 +255,16 @@ void C3D_FrameEnd(u8 flags)
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;
frameStage |= STAGE_HAS_TRANSFER(i);
}
GPUCMD_SetBuffer(ctx->cmdBuf, ctx->cmdBufSize, 0);
measureGpuTime = true;
osTickCounterStart(&gpuTime);
gxCmdQueueRun(&ctx->gxQueue);
@ -277,6 +307,8 @@ static void C3Di_RenderTargetFinishInit(C3D_RenderTarget* target)
C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF colorFmt, C3D_DEPTHTYPE depthFmt)
{
if (!checkRenderQueueInit()) goto _fail0;
GPU_DEPTHBUF depthFmtReal = GPU_RB_DEPTH16;
void* depthBuf = NULL;
void* colorBuf = vramAlloc(C3D_CalcColorBufSize(width,height,colorFmt));
@ -284,10 +316,7 @@ C3D_RenderTarget* C3D_RenderTargetCreate(int width, int height, GPU_COLORBUF col
if (C3D_DEPTHTYPE_OK(depthFmt))
{
depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
size_t depthSize = C3D_CalcDepthBufSize(width,height,depthFmtReal);
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
depthBuf = vramAlloc(C3D_CalcDepthBufSize(width,height,depthFmtReal));
if (!depthBuf) goto _fail1;
}
@ -316,7 +345,8 @@ _fail0:
C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face, int level, C3D_DEPTHTYPE depthFmt)
{
if (!addrIsVRAM(tex->data)) return NULL; // Render targets must be in VRAM
if (!checkRenderQueueInit()) return NULL;
C3D_RenderTarget* target = C3Di_RenderTargetNew();
if (!target) return NULL;
@ -326,10 +356,7 @@ C3D_RenderTarget* C3D_RenderTargetCreateFromTex(C3D_Tex* tex, GPU_TEXFACE face,
if (C3D_DEPTHTYPE_OK(depthFmt))
{
GPU_DEPTHBUF depthFmtReal = C3D_DEPTHTYPE_VAL(depthFmt);
size_t depthSize = C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal);
vramAllocPos vramBank = addrGetVRAMBank(tex->data);
void* depthBuf = vramAllocAt(depthSize, vramBank ^ VRAM_ALLOC_ANY); // Attempt opposite bank first...
if (!depthBuf) depthBuf = vramAllocAt(depthSize, vramBank); // ... if that fails, attempt same bank
void* depthBuf = vramAlloc(C3D_CalcDepthBufSize(fb->width,fb->height,depthFmtReal));
if (!depthBuf)
{
free(target);
@ -362,10 +389,7 @@ 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_WaitAndClearQueue(-1);
C3Di_RenderTargetDestroy(target);
}
@ -375,19 +399,12 @@ void C3D_RenderTargetSetOutput(C3D_RenderTarget* target, gfxScreen_t screen, gfx
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;
}
target->linked = true;
target->transferFlags = transferFlags;
target->screen = screen;
target->side = side;
}
static void C3Di_SafeDisplayTransfer(u32* inadr, u32 indim, u32* outadr, u32 outdim, u32 flags)

View File

@ -30,6 +30,12 @@ static inline size_t fmtSize(GPU_TEXCOLOR fmt)
}
}
static inline bool addrIsVRAM(const void* addr)
{
u32 vaddr = (u32)addr;
return vaddr >= 0x1F000000 && vaddr < 0x1F600000;
}
static inline bool checkTexSize(u32 size)
{
if (size < 8 || size > 1024)

View File

@ -47,10 +47,10 @@ ICON :=
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -g -Wall -O3 -mword-relocations \
-ffunction-sections \
-fomit-frame-pointer -ffunction-sections \
$(ARCH)
CFLAGS += $(INCLUDE) -D__3DS__
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11