3DS: Port to using citro3D

This commit is contained in:
Jeffrey Pfau 2016-07-27 01:04:52 -07:00
parent 2f3c4982bd
commit 7930c5a350
8 changed files with 312 additions and 505 deletions

View File

@ -23,6 +23,7 @@ Misc:
- Debugger: Support register and memory writes via GDB stub
- GBA Audio: Force audio DMAs to not increment destination
- Qt: Thread startup improvements
- 3DS: Port to using citro3D
0.4.1: (2016-07-11)
Bugfixes:

View File

@ -13,7 +13,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format" PARENT_SCOPE)
set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 IOAPI_NO_64)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
list(APPEND OS_LIB ctru)
list(APPEND OS_LIB citro3d ctru)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/3ds-*.c ${CMAKE_CURRENT_SOURCE_DIR}/ctru-heap.c ${CMAKE_CURRENT_SOURCE_DIR}/socket.c)
set(OS_SRC ${OS_SRC} PARENT_SCOPE)
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
@ -31,9 +31,12 @@ set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
list(APPEND GUI_SRC
${CMAKE_CURRENT_BINARY_DIR}/icons.c
${CMAKE_CURRENT_BINARY_DIR}/font.c
${CMAKE_CURRENT_BINARY_DIR}/uishader.c
${CMAKE_CURRENT_BINARY_DIR}/uishader.h
${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h
${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c
${CMAKE_CURRENT_SOURCE_DIR}/ctr-gpu.c
@ -42,10 +45,14 @@ list(APPEND GUI_SRC
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/icons.c
${CMAKE_CURRENT_BINARY_DIR}/font.c
${CMAKE_CURRENT_BINARY_DIR}/uishader.c
${CMAKE_CURRENT_BINARY_DIR}/uishader.h
${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h
${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h
PROPERTIES GENERATED ON)
add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${OS_LIB})
@ -67,20 +74,36 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.vsh
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica
COMMAND ${PICASSO}
-o ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
-h ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
${CMAKE_CURRENT_SOURCE_DIR}/uishader.vsh
COMMENT "picasso uishader.vsh")
-o ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin
-h ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin.h
${CMAKE_CURRENT_SOURCE_DIR}/uishader.g.pica
COMMENT "picasso uishader.g.pica")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.c ${CMAKE_CURRENT_BINARY_DIR}/uishader.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.c ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader_g.shbin
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "raw2c uishader.shbin")
COMMENT "raw2c uishader.g.shbin")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica
COMMAND ${PICASSO}
-o ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin
-h ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin.h
${CMAKE_CURRENT_SOURCE_DIR}/uishader.v.pica
COMMENT "picasso uishader.v.pica")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.c ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.h
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/uishader_v.shbin
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "raw2c uishader.v.shbin")
add_custom_target(${BINARY_NAME}.3dsx ALL
${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx --smdh=${BINARY_NAME}.smdh

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2015 Yuri Kunde Schlesner
* Copyright (c) 2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -13,348 +14,132 @@
#include "ctr-gpu.h"
#include "uishader.h"
#include "uishader.shbin.h"
#include "uishader_v.h"
#include "uishader_v.shbin.h"
#include "uishader_g.h"
#include "uishader_g.shbin.h"
struct ctrUIVertex {
s16 x,y;
s16 u,v;
short x, y;
short w, h;
short u, v;
short uw, vh;
u32 abgr;
};
#define VRAM_BASE 0x18000000u
#define MAX_NUM_QUADS 1024
#define COMMAND_LIST_LENGTH (16 * 1024)
// Each quad requires 4 vertices and 2*3 indices for the two triangles used to draw it
#define VERTEX_INDEX_BUFFER_SIZE (MAX_NUM_QUADS * (4 * sizeof(struct ctrUIVertex) + 6 * sizeof(u16)))
#define VERTEX_BUFFER_SIZE MAX_NUM_QUADS * sizeof(struct ctrUIVertex)
static struct ctrUIVertex* ctrVertexBuffer = NULL;
static u16* ctrIndexBuffer = NULL;
static u16 ctrNumQuads = 0;
static void* gpuColorBuffer[2] = { NULL, NULL };
static u32* gpuCommandList = NULL;
static void* screenTexture = NULL;
static C3D_Tex* activeTexture = NULL;
static shaderProgram_s gpuShader;
static DVLB_s* passthroughShader = NULL;
static int pendingEvents = 0;
static const struct ctrTexture* activeTexture = NULL;
void ctrClearPending(int events) {
int toClear = events & pendingEvents;
if (toClear & (1 << GSPGPU_EVENT_PSC0)) {
gspWaitForPSC0();
}
if (toClear & (1 << GSPGPU_EVENT_PPF)) {
gspWaitForPPF();
}
pendingEvents ^= toClear;
}
// Replacements for the limiting GPU_SetViewport function in ctrulib
static void _GPU_SetFramebuffer(intptr_t colorBuffer, intptr_t depthBuffer, u16 w, u16 h) {
u32 buf[4];
// Unknown
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001);
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 0x00000001);
// Set depth/color buffer address and dimensions
buf[0] = depthBuffer >> 3;
buf[1] = colorBuffer >> 3;
buf[2] = (0x01) << 24 | ((h-1) & 0xFFF) << 12 | (w & 0xFFF) << 0;
GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, buf, 3);
GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, buf[2]);
// Set depth/color buffer pixel format
GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, 3 /* D248S */ );
GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, 0 /* RGBA8 */ << 16 | 2 /* Unknown */);
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, 0); // Unknown
// Enable color/depth buffers
buf[0] = colorBuffer != 0 ? 0xF : 0x0;
buf[1] = buf[0];
buf[2] = depthBuffer != 0 ? 0x2 : 0x0;
buf[3] = buf[2];
GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, buf, 4);
}
static void _GPU_SetViewportEx(u16 x, u16 y, u16 w, u16 h) {
u32 buf[4];
buf[0] = f32tof24(w / 2.0f);
buf[1] = f32tof31(2.0f / w) << 1;
buf[2] = f32tof24(h / 2.0f);
buf[3] = f32tof31(2.0f / h) << 1;
GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, buf, 4);
GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, (y & 0xFFFF) << 16 | (x & 0xFFFF) << 0);
buf[0] = 0;
buf[1] = 0;
buf[2] = ((h-1) & 0xFFFF) << 16 | ((w-1) & 0xFFFF) << 0;
GPUCMD_AddIncrementalWrites(GPUREG_SCISSORTEST_MODE, buf, 3);
}
static void _setDummyTexEnv(int id) {
GPU_SetTexEnv(id,
GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0),
GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0),
GPU_TEVOPERANDS(0, 0, 0),
GPU_TEVOPERANDS(0, 0, 0),
GPU_REPLACE,
GPU_REPLACE,
0x00000000);
}
Result ctrInitGpu() {
Result res = -1;
// Allocate buffers
gpuColorBuffer[0] = vramAlloc(400 * 240 * 4);
gpuColorBuffer[1] = vramAlloc(320 * 240 * 4);
gpuCommandList = linearAlloc(COMMAND_LIST_LENGTH * sizeof(u32));
ctrVertexBuffer = linearAlloc(VERTEX_INDEX_BUFFER_SIZE);
if (gpuColorBuffer[0] == NULL || gpuColorBuffer[1] == NULL || gpuCommandList == NULL || ctrVertexBuffer == NULL) {
res = -1;
goto error_allocs;
}
// Both buffers share the same allocation, index buffer follows the vertex buffer
ctrIndexBuffer = (u16*)(ctrVertexBuffer + (4 * MAX_NUM_QUADS));
static DVLB_s* vertexShader = NULL;
static DVLB_s* geometryShader = NULL;
bool ctrInitGpu() {
// Load vertex shader binary
passthroughShader = DVLB_ParseFile((u32*)uishader, uishader_size);
if (passthroughShader == NULL) {
res = -1;
goto error_dvlb;
vertexShader = DVLB_ParseFile((u32*) uishader_v, uishader_v_size);
if (vertexShader == NULL) {
return false;
}
// Load geometry shader binary
geometryShader = DVLB_ParseFile((u32*) uishader_g, uishader_g_size);
if (geometryShader == NULL) {
return false;
}
// Create shader
shaderProgramInit(&gpuShader);
res = shaderProgramSetVsh(&gpuShader, &passthroughShader->DVLE[0]);
Result res = shaderProgramSetVsh(&gpuShader, &vertexShader->DVLE[0]);
if (res < 0) {
goto error_shader;
return false;
}
res = shaderProgramSetGsh(&gpuShader, &geometryShader->DVLE[0], 3);
if (res < 0) {
return false;
}
C3D_BindProgram(&gpuShader);
// Allocate buffers
ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE);
if (ctrVertexBuffer == NULL) {
return false;
}
// Initialize the GPU in ctrulib and assign the command buffer to accept submission of commands
GPU_Init(NULL);
GPUCMD_SetBuffer(gpuCommandList, COMMAND_LIST_LENGTH, 0);
// Set up TexEnv and other parameters
C3D_TexEnv* env = C3D_GetTexEnv(0);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
return 0;
C3D_CullFace(GPU_CULL_NONE);
C3D_DepthTest(false, GPU_ALWAYS, GPU_WRITE_ALL);
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_AlphaTest(false, GPU_ALWAYS, 0);
C3D_BlendingColor(0);
error_shader:
shaderProgramFree(&gpuShader);
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos
AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0
AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
error_dvlb:
if (passthroughShader != NULL) {
DVLB_Free(passthroughShader);
passthroughShader = NULL;
}
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 3, 0x210);
error_allocs:
if (ctrVertexBuffer != NULL) {
linearFree(ctrVertexBuffer);
ctrVertexBuffer = NULL;
ctrIndexBuffer = NULL;
}
if (gpuCommandList != NULL) {
GPUCMD_SetBuffer(NULL, 0, 0);
linearFree(gpuCommandList);
gpuCommandList = NULL;
}
if (gpuColorBuffer[0] != NULL) {
vramFree(gpuColorBuffer[0]);
gpuColorBuffer[0] = NULL;
}
if (gpuColorBuffer[1] != NULL) {
vramFree(gpuColorBuffer[1]);
gpuColorBuffer[1] = NULL;
}
return res;
return true;
}
void ctrDeinitGpu() {
if (ctrVertexBuffer) {
linearFree(ctrVertexBuffer);
ctrVertexBuffer = NULL;
}
shaderProgramFree(&gpuShader);
DVLB_Free(passthroughShader);
passthroughShader = NULL;
linearFree(screenTexture);
screenTexture = NULL;
linearFree(ctrVertexBuffer);
ctrVertexBuffer = NULL;
ctrIndexBuffer = NULL;
GPUCMD_SetBuffer(NULL, 0, 0);
linearFree(gpuCommandList);
gpuCommandList = NULL;
vramFree(gpuColorBuffer[0]);
gpuColorBuffer[0] = NULL;
vramFree(gpuColorBuffer[1]);
gpuColorBuffer[1] = NULL;
}
void ctrGpuBeginFrame(int screen) {
if (screen > 1) {
return;
if (vertexShader) {
DVLB_Free(vertexShader);
vertexShader = NULL;
}
int fw;
if (screen == 0) {
fw = 400;
} else {
fw = 320;
if (geometryShader) {
DVLB_Free(geometryShader);
geometryShader = NULL;
}
_GPU_SetFramebuffer(osConvertVirtToPhys(gpuColorBuffer[screen]), 0, 240, fw);
}
void ctrGpuBeginDrawing(void) {
shaderProgramUse(&gpuShader);
// Disable depth and stencil testing
GPU_SetDepthTestAndWriteMask(false, GPU_ALWAYS, GPU_WRITE_COLOR);
GPU_SetStencilTest(false, GPU_ALWAYS, 0, 0xFF, 0);
GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP);
GPU_DepthMap(-1.0f, 0.0f);
// Enable alpha blending
GPU_SetAlphaBlending(
GPU_BLEND_ADD, GPU_BLEND_ADD, // Operation RGB, Alpha
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, // Color src, dst
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); // Alpha src, dst
GPU_SetBlendingColor(0, 0, 0, 0);
// Disable alpha testing
GPU_SetAlphaTest(false, GPU_ALWAYS, 0);
// Unknown
GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0);
GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0);
GPU_SetTexEnv(0,
GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0), // RGB
GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0), // Alpha
GPU_TEVOPERANDS(0, 0, 0), // RGB
GPU_TEVOPERANDS(0, 0, 0), // Alpha
GPU_MODULATE, GPU_MODULATE, // Operation RGB, Alpha
0x00000000); // Constant color
_setDummyTexEnv(1);
_setDummyTexEnv(2);
_setDummyTexEnv(3);
_setDummyTexEnv(4);
_setDummyTexEnv(5);
// Configure vertex attribute format
u32 bufferOffsets[] = { osConvertVirtToPhys(ctrVertexBuffer) - VRAM_BASE };
u64 arrayTargetAttributes[] = { 0x210 };
u8 numAttributesInArray[] = { 3 };
GPU_SetAttributeBuffers(
3, // Number of attributes
(u32*)VRAM_BASE, // Base address
GPU_ATTRIBFMT(0, 2, GPU_SHORT) | // Attribute format
GPU_ATTRIBFMT(1, 2, GPU_SHORT) |
GPU_ATTRIBFMT(2, 4, GPU_UNSIGNED_BYTE),
0xFF8, // Non-fixed vertex inputs
0x210, // Vertex shader input map
1, // Use 1 vertex array
bufferOffsets, arrayTargetAttributes, numAttributesInArray);
}
void ctrGpuEndFrame(int screen, void* outputFramebuffer, int w, int h) {
if (screen > 1) {
return;
}
int fw;
if (screen == 0) {
fw = 400;
} else {
fw = 320;
}
ctrFlushBatch();
void* colorBuffer = (u8*)gpuColorBuffer[screen] + ((fw - w) * 240 * 4);
const u32 GX_CROP_INPUT_LINES = (1 << 2);
ctrClearPending(1 << GSPGPU_EVENT_PSC0);
ctrClearPending(1 << GSPGPU_EVENT_PPF);
GX_DisplayTransfer(
colorBuffer, GX_BUFFER_DIM(240, fw),
outputFramebuffer, GX_BUFFER_DIM(h, w),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) |
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) |
GX_CROP_INPUT_LINES);
pendingEvents |= (1 << GSPGPU_EVENT_PPF);
}
void ctrGpuEndDrawing(void) {
ctrClearPending(1 << GSPGPU_EVENT_PPF);
gfxSwapBuffersGpu();
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
void* gpuColorBuffer0End = (char*)gpuColorBuffer[0] + 240 * 400 * 4;
void* gpuColorBuffer1End = (char*)gpuColorBuffer[1] + 240 * 320 * 4;
GX_MemoryFill(
gpuColorBuffer[0], 0x00000000, gpuColorBuffer0End, GX_FILL_32BIT_DEPTH | GX_FILL_TRIGGER,
gpuColorBuffer[1], 0x00000000, gpuColorBuffer1End, GX_FILL_32BIT_DEPTH | GX_FILL_TRIGGER);
pendingEvents |= 1 << GSPGPU_EVENT_PSC0;
}
void ctrSetViewportSize(s16 w, s16 h) {
// Set up projection matrix mapping (0,0) to the top-left and (w,h) to the
// bottom-right, taking into account the 3DS' screens' portrait
// orientation.
float projectionMtx[4 * 4] = {
// Rows are in the order w z y x, because ctrulib
1.0f, 0.0f, -2.0f / h, 0.0f,
1.0f, 0.0f, 0.0f, -2.0f / w,
-0.5f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
};
GPU_SetFloatUniform(GPU_VERTEX_SHADER, VSH_FVEC_projectionMtx, (u32*)&projectionMtx, 4);
_GPU_SetViewportEx(0, 0, h, w);
C3D_SetViewport(0, 0, h, w);
C3D_Mtx projectionMtx;
Mtx_OrthoTilt(&projectionMtx, 0.0, w, h, 0.0, 0.0, 1.0);
C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx);
}
void ctrActivateTexture(const struct ctrTexture* texture) {
if (activeTexture == texture) {
void ctrActivateTexture(C3D_Tex* texture) {
if (texture == activeTexture) {
return;
}
if (activeTexture) {
ctrFlushBatch();
}
ctrFlushBatch();
GPU_SetTextureEnable(GPU_TEXUNIT0);
GPU_SetTexture(
GPU_TEXUNIT0, (u32*)osConvertVirtToPhys(texture->data),
texture->width, texture->height,
GPU_TEXTURE_MAG_FILTER(texture->filter) | GPU_TEXTURE_MIN_FILTER(texture->filter) |
GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER),
texture->format);
GPU_SetTextureBorderColor(GPU_TEXUNIT0, 0x00000000);
float textureMtx[2 * 4] = {
// Rows are in the order w z y x, because ctrulib
0.0f, 0.0f, 0.0f, 1.0f / texture->width,
0.0f, 0.0f, 1.0f / texture->height, 0.0f,
};
GPU_SetFloatUniform(GPU_VERTEX_SHADER, VSH_FVEC_textureMtx, (u32*)&textureMtx, 2);
C3D_TexBind(0, texture);
activeTexture = texture;
C3D_Mtx textureMtx = {
.m = {
// Rows are in the order w z y x, because ctrulib
0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width,
0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f
}
};
C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx);
}
void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh) {
@ -362,38 +147,22 @@ void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 u
ctrFlushBatch();
}
u16 index = ctrNumQuads * 4;
struct ctrUIVertex* vtx = &ctrVertexBuffer[index];
vtx->x = x; vtx->y = y;
vtx->u = u; vtx->v = v;
vtx->abgr = color;
vtx++;
vtx->x = x + w; vtx->y = y;
vtx->u = u + uw; vtx->v = v;
vtx->abgr = color;
vtx++;
vtx->x = x; vtx->y = y + h;
vtx->u = u; vtx->v = v + vh;
vtx->abgr = color;
vtx++;
vtx->x = x + w; vtx->y = y + h;
vtx->u = u + uw; vtx->v = v + vh;
struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrNumQuads];
vtx->x = x;
vtx->y = y;
vtx->w = w;
vtx->h = h;
vtx->u = u;
vtx->v = v;
vtx->uw = uw;
vtx->vh = vh;
vtx->abgr = color;
u16* i = &ctrIndexBuffer[ctrNumQuads * 6];
i[0] = index + 0; i[1] = index + 1; i[2] = index + 2;
i[3] = index + 2; i[4] = index + 1; i[5] = index + 3;
ctrNumQuads += 1;
++ctrNumQuads;
}
void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
ctrAddRectScaled(color,
x, y, w, h,
u, v, w, h);
ctrAddRectScaled(color, x, y, w, h, u, v, w, h);
}
void ctrFlushBatch(void) {
@ -401,19 +170,9 @@ void ctrFlushBatch(void) {
return;
}
ctrClearPending((1 << GSPGPU_EVENT_PSC0));
GSPGPU_FlushDataCache(ctrVertexBuffer, VERTEX_INDEX_BUFFER_SIZE);
GPU_DrawElements(GPU_GEOMETRY_PRIM, (u32*)(osConvertVirtToPhys(ctrIndexBuffer) - VRAM_BASE), ctrNumQuads * 6);
GPU_FinishDrawing();
GPUCMD_Finalize();
GSPGPU_FlushDataCache((u8*)gpuCommandList, COMMAND_LIST_LENGTH * sizeof(u32));
GPUCMD_FlushAndRun();
gspWaitForP3D();
GPUCMD_SetBufferOffset(0);
GSPGPU_FlushDataCache(ctrVertexBuffer, VERTEX_BUFFER_SIZE);
C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumQuads);
C3D_Flush();
ctrNumQuads = 0;
}

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2015 Yuri Kunde Schlesner
* Copyright (c) 2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -8,34 +9,14 @@
#define GUI_GPU_H
#include <3ds.h>
#include <citro3d.h>
struct ctrTexture {
void* data;
u32 format;
u32 filter;
u16 width;
u16 height;
};
static inline void ctrTexture_Init(struct ctrTexture* tex) {
tex->data = NULL;
tex->format = GPU_RGB565;
tex->filter = GPU_NEAREST;
tex->width = 0;
tex->height = 0;
}
Result ctrInitGpu(void);
bool ctrInitGpu(void);
void ctrDeinitGpu(void);
void ctrGpuBeginDrawing(void);
void ctrGpuBeginFrame(int screen);
void ctrGpuEndFrame(int screen, void* outputFramebuffer, int w, int h);
void ctrGpuEndDrawing(void);
void ctrSetViewportSize(s16 w, s16 h);
void ctrActivateTexture(const struct ctrTexture* texture);
void ctrActivateTexture(C3D_Tex* texture);
void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh);
void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h);
void ctrFlushBatch(void);

View File

@ -7,17 +7,18 @@
#include "util/gui/font-metrics.h"
#include "util/png-io.h"
#include "util/vfs.h"
#include "platform/3ds/ctr-gpu.h"
#include "icons.h"
#include "font.h"
#include "ctr-gpu.h"
#define CELL_HEIGHT 16
#define CELL_WIDTH 16
#define GLYPH_HEIGHT 12
struct GUIFont {
struct ctrTexture texture;
struct ctrTexture icons;
C3D_Tex texture;
C3D_Tex icons;
};
struct GUIFont* GUIFontCreate(void) {
@ -26,23 +27,16 @@ struct GUIFont* GUIFontCreate(void) {
return 0;
}
struct ctrTexture* tex = &guiFont->texture;
ctrTexture_Init(tex);
tex->data = vramAlloc(256 * 128 * 2);
tex->format = GPU_RGBA5551;
tex->width = 256;
tex->height = 128;
C3D_Tex* tex = &guiFont->texture;
C3D_TexInitVRAM(tex, 256, 128, GPU_RGBA5551);
GSPGPU_FlushDataCache(font, font_size);
GX_RequestDma((u32*) font, tex->data, font_size);
gspWaitForDMA();
tex = &guiFont->icons;
ctrTexture_Init(tex);
tex->data = vramAlloc(256 * 64 * 2);
tex->format = GPU_RGBA5551;
tex->width = 256;
tex->height = 64;
C3D_TexInitVRAM(tex, 256, 64, GPU_RGBA5551);
GSPGPU_FlushDataCache(icons, icons_size);
GX_RequestDma((u32*) icons, tex->data, icons_size);
@ -52,8 +46,8 @@ struct GUIFont* GUIFontCreate(void) {
}
void GUIFontDestroy(struct GUIFont* font) {
vramFree(font->texture.data);
vramFree(font->icons.data);
C3D_TexDelete(&font->texture);
C3D_TexDelete(&font->icons);
free(font);
}

View File

@ -61,39 +61,39 @@ static struct mAVStream stream;
static int16_t* audioLeft = 0;
static int16_t* audioRight = 0;
static size_t audioPos = 0;
static struct ctrTexture gbaOutputTexture;
static int guiDrawn;
static int screenCleanup;
static C3D_Tex outputTexture;
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
static int bufferId = 0;
static C3D_RenderBuf bottomScreen;
static C3D_RenderBuf topScreen;
static aptHookCookie cookie;
enum {
GUI_ACTIVE = 1,
GUI_THIS_FRAME = 2,
};
enum {
SCREEN_CLEANUP_TOP_1 = 1,
SCREEN_CLEANUP_TOP_2 = 2,
SCREEN_CLEANUP_TOP = SCREEN_CLEANUP_TOP_1 | SCREEN_CLEANUP_TOP_2,
SCREEN_CLEANUP_BOTTOM_1 = 4,
SCREEN_CLEANUP_BOTTOM_2 = 8,
SCREEN_CLEANUP_BOTTOM = SCREEN_CLEANUP_BOTTOM_1 | SCREEN_CLEANUP_BOTTOM_2,
};
extern bool allocateRomBuffer(void);
static bool _initGpu(void) {
if (!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) {
return false;
}
if (!C3D_RenderBufInit(&topScreen, 240, 400, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&bottomScreen, 240, 320, GPU_RB_RGB8, 0)) {
return false;
}
return ctrInitGpu();
}
static void _cleanup(void) {
ctrDeinitGpu();
if (outputBuffer) {
linearFree(outputBuffer);
}
if (gbaOutputTexture.data) {
ctrDeinitGpu();
vramFree(gbaOutputTexture.data);
}
C3D_RenderBufDelete(&topScreen);
C3D_RenderBufDelete(&bottomScreen);
C3D_Fini();
gfxExit();
@ -173,52 +173,15 @@ static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, voi
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
static void _drawStart(void) {
ctrGpuBeginDrawing();
if (screenMode < SM_PA_TOP || (guiDrawn & GUI_ACTIVE)) {
ctrGpuBeginFrame(GFX_BOTTOM);
ctrSetViewportSize(320, 240);
} else {
ctrGpuBeginFrame(GFX_TOP);
ctrSetViewportSize(400, 240);
}
guiDrawn &= ~GUI_THIS_FRAME;
C3D_RenderBufClear(&bottomScreen);
C3D_RenderBufClear(&topScreen);
}
static void _drawEnd(void) {
int screen = screenMode < SM_PA_TOP ? GFX_BOTTOM : GFX_TOP;
u16 width = 0, height = 0;
void* outputFramebuffer = gfxGetFramebuffer(screen, GFX_LEFT, &height, &width);
ctrGpuEndFrame(screen, outputFramebuffer, width, height);
if (screen != GFX_BOTTOM) {
if (guiDrawn & (GUI_THIS_FRAME | GUI_ACTIVE)) {
void* outputFramebuffer = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &height, &width);
ctrGpuEndFrame(GFX_BOTTOM, outputFramebuffer, width, height);
} else if (screenCleanup & SCREEN_CLEANUP_BOTTOM) {
ctrGpuBeginFrame(GFX_BOTTOM);
if (screenCleanup & SCREEN_CLEANUP_BOTTOM_1) {
screenCleanup &= ~SCREEN_CLEANUP_BOTTOM_1;
} else if (screenCleanup & SCREEN_CLEANUP_BOTTOM_2) {
screenCleanup &= ~SCREEN_CLEANUP_BOTTOM_2;
}
void* outputFramebuffer = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &height, &width);
ctrGpuEndFrame(GFX_BOTTOM, outputFramebuffer, width, height);
}
}
if ((screenCleanup & SCREEN_CLEANUP_TOP) && screen != GFX_TOP) {
ctrGpuBeginFrame(GFX_TOP);
if (screenCleanup & SCREEN_CLEANUP_TOP_1) {
screenCleanup &= ~SCREEN_CLEANUP_TOP_1;
} else if (screenCleanup & SCREEN_CLEANUP_TOP_2) {
screenCleanup &= ~SCREEN_CLEANUP_TOP_2;
}
void* outputFramebuffer = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &height, &width);
ctrGpuEndFrame(GFX_TOP, outputFramebuffer, width, height);
}
ctrGpuEndDrawing();
C3D_RenderBufTransfer(&topScreen, (u32*) gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
C3D_RenderBufTransfer(&bottomScreen, (u32*) gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
gfxSwapBuffersGpu();
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
}
static int _batteryState(void) {
@ -237,20 +200,17 @@ static int _batteryState(void) {
}
static void _guiPrepare(void) {
guiDrawn = GUI_ACTIVE | GUI_THIS_FRAME;
int screen = screenMode < SM_PA_TOP ? GFX_BOTTOM : GFX_TOP;
if (screen == GFX_BOTTOM) {
return;
}
ctrFlushBatch();
ctrGpuBeginFrame(GFX_BOTTOM);
C3D_RenderBufBind(&bottomScreen);
ctrSetViewportSize(320, 240);
}
static void _guiFinish(void) {
guiDrawn &= ~GUI_ACTIVE;
screenCleanup |= SCREEN_CLEANUP_BOTTOM;
ctrFlushBatch();
}
static void _setup(struct mGUIRunner* runner) {
@ -322,7 +282,6 @@ static void _gameLoaded(struct mGUIRunner* runner) {
unsigned mode;
if (mCoreConfigGetUIntValue(&runner->core->config, "screenMode", &mode) && mode != screenMode) {
screenMode = mode;
screenCleanup |= SCREEN_CLEANUP_BOTTOM | SCREEN_CLEANUP_TOP;
}
}
@ -358,6 +317,15 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
}
static void _drawTex(struct mCore* core, bool faded) {
if (screenMode < SM_PA_TOP) {
C3D_RenderBufBind(&bottomScreen);
ctrSetViewportSize(320, 240);
} else {
C3D_RenderBufBind(&topScreen);
ctrSetViewportSize(400, 240);
}
ctrActivateTexture(&outputTexture);
u32 color = faded ? 0x3FFFFFFF : 0xFFFFFFFF;
int screen_w = screenMode < SM_PA_TOP ? 320 : 400;
@ -408,15 +376,16 @@ static void _drawTex(struct mCore* core, bool faded) {
int y = (screen_h - h) / 2;
ctrAddRectScaled(color, x, y, w, h, 0, 0, corew, coreh);
ctrFlushBatch();
}
static void _drawFrame(struct mGUIRunner* runner, bool faded) {
UNUSED(runner);
struct ctrTexture* tex = &gbaOutputTexture;
C3D_Tex* tex = &outputTexture;
GSPGPU_FlushDataCache(outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
GX_DisplayTransfer(
C3D_SafeDisplayTransfer(
outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
@ -429,14 +398,13 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
}
gspWaitForPPF();
ctrActivateTexture(tex);
_drawTex(runner->core, faded);
}
static void _drawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, bool faded) {
UNUSED(runner);
struct ctrTexture* tex = &gbaOutputTexture;
C3D_Tex* tex = &outputTexture;
u16* newPixels = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * sizeof(u32), 0x100);
@ -454,7 +422,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, b
}
GSPGPU_FlushDataCache(newPixels, 256 * VIDEO_VERTICAL_PIXELS * sizeof(u32));
GX_DisplayTransfer(
C3D_SafeDisplayTransfer(
(u32*) newPixels, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
@ -463,7 +431,6 @@ static void _drawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, b
gspWaitForPPF();
linearFree(newPixels);
ctrActivateTexture(tex);
_drawTex(runner->core, faded);
}
@ -479,9 +446,11 @@ static uint16_t _pollGameInput(struct mGUIRunner* runner) {
static void _incrementScreenMode(struct mGUIRunner* runner) {
UNUSED(runner);
screenCleanup |= SCREEN_CLEANUP_TOP | SCREEN_CLEANUP_BOTTOM;
screenMode = (screenMode + 1) % SM_MAX;
mCoreConfigSetUIntValue(&runner->core->config, "screenMode", screenMode);
C3D_RenderBufClear(&bottomScreen);
C3D_RenderBufClear(&topScreen);
}
static uint32_t _pollInput(void) {
@ -636,39 +605,29 @@ int main() {
audioRight = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
}
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, false);
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true);
if (ctrInitGpu() < 0) {
gbaOutputTexture.data = 0;
if (!_initGpu()) {
outputTexture.data = 0;
_cleanup();
return 1;
}
ctrTexture_Init(&gbaOutputTexture);
gbaOutputTexture.format = GPU_RGB565;
gbaOutputTexture.filter = GPU_LINEAR;
gbaOutputTexture.width = 256;
gbaOutputTexture.height = 256;
gbaOutputTexture.data = vramAlloc(256 * 256 * 2);
void* outputTextureEnd = (u8*)gbaOutputTexture.data + 256 * 256 * 2;
if (!gbaOutputTexture.data) {
if (!C3D_TexInitVRAM(&outputTexture, 256, 256, GPU_RGB565)) {
_cleanup();
return 1;
}
C3D_TexSetWrap(&outputTexture, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
C3D_TexSetFilter(&outputTexture, GPU_LINEAR, GPU_LINEAR);
void* outputTextureEnd = (u8*)outputTexture.data + 256 * 256 * 2;
// Zero texture data to make sure no garbage around the border interferes with filtering
GX_MemoryFill(
gbaOutputTexture.data, 0x0000, outputTextureEnd, GX_FILL_16BIT_DEPTH | GX_FILL_TRIGGER,
outputTexture.data, 0x0000, outputTextureEnd, GX_FILL_16BIT_DEPTH | GX_FILL_TRIGGER,
NULL, 0, NULL, 0);
gspWaitForPSC0();
sdmcArchive = (FS_Archive) {
ARCHIVE_SDMC,
(FS_Path) { PATH_EMPTY, 1, "" },
0
};
FSUSER_OpenArchive(&sdmcArchive);
FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
struct GUIFont* font = GUIFontCreate();

View File

@ -0,0 +1,96 @@
; Copyright (c) 2015 Yuri Kunde Schlesner
; Copyright (c) 2016 Jeffrey Pfau
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
; Uniforms
.fvec projectionMtx[4]
.fvec textureMtx[2]
; Constants
.constf consts1(0.0, 1.0, -0.5, -1.0)
; Outputs : here only position and color
.out out_pos position
.out out_tc0 texcoord0
.out out_col color
; Inputs : here we have only vertices
.alias in_pos v0
.alias in_tc0 v1
.alias in_col v2
.gsh
.proc main
; Set up the vertex endpoints
mov r0.xy, in_pos.xy
mov r0.zw, consts1.zy
add r1.xy, r0.xy, in_pos.zw
dp4 r2.x, projectionMtx[0], r0
dp4 r2.y, projectionMtx[1], r0
dp4 r2.z, projectionMtx[2], r0
dp4 r2.w, projectionMtx[3], r0
dp4 r3.x, projectionMtx[0], r1
dp4 r3.y, projectionMtx[1], r1
dp4 r3.z, projectionMtx[2], r1
dp4 r3.w, projectionMtx[3], r1
; Set up the texture endpoints
mov r0.xy, in_tc0.xy
mov r0.zw, consts1.xy
add r1.xy, r0.xy, in_tc0.zw
dp4 r4.x, textureMtx[0], r0
dp4 r4.y, textureMtx[1], r0
mov r4.zw, consts1.xy
dp4 r5.x, textureMtx[0], r1
dp4 r5.y, textureMtx[1], r1
mov r5.zw, consts1.xy
; Emit top-left
setemit 0
mov out_pos.xyzw, r2.xyzw
mov out_tc0.xyzw, r4.xyzw
mov out_col, in_col
emit
; Emit bottom-left
setemit 1
mov out_pos.x, r2.x
mov out_pos.y, r3.y
mov out_pos.z, consts1.z
mov out_pos.w, consts1.y
mov out_tc0.x, r5.x
mov out_tc0.y, r4.y
mov out_tc0.z, consts1.x
mov out_tc0.w, consts1.y
mov out_col, in_col
emit
; Emit bottom-right
setemit 2, prim
mov out_pos.xyzw, r3.xyzw
mov out_tc0.xyzw, r5.xyzw
mov out_col, in_col
emit
; Emit top-right
setemit 1, prim inv
mov out_pos.x, r3.x
mov out_pos.y, r2.y
mov out_pos.z, consts1.z
mov out_pos.w, consts1.y
mov out_tc0.x, r4.x
mov out_tc0.y, r5.y
mov out_tc0.z, consts1.x
mov out_tc0.w, consts1.y
mov out_col, in_col
emit
end
.end

View File

@ -1,4 +1,6 @@
; Copyright (c) 2015 Yuri Kunde Schlesner
; Copyright (c) 2016 Jeffrey Pfau
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
@ -8,31 +10,23 @@
; corresponding matrices before outputting
; Uniforms
.fvec projectionMtx[4]
.fvec textureMtx[2]
; Constants
.constf consts1(0.0, 1.0, 0.0039215686, 0.0)
.constf consts1(0.0, 1.0, 0.0039215686, -1.0)
; Outputs : here only position and color
; Outputs
.out out_pos position
.out out_tc0 texcoord0
.out out_col color
; Inputs : here we have only vertices
; Inputs
.alias in_pos v0
.alias in_tc0 v1
.alias in_col v2
.proc main
dp4 out_pos.x, projectionMtx[0], in_pos
dp4 out_pos.y, projectionMtx[1], in_pos
dp4 out_pos.z, projectionMtx[2], in_pos
dp4 out_pos.w, projectionMtx[3], in_pos
dp4 out_tc0.x, textureMtx[0], in_tc0
dp4 out_tc0.y, textureMtx[1], in_tc0
mov out_tc0.zw, consts1.xxxy
mov out_pos, in_pos
mov out_tc0, in_tc0
; Normalize color by multiplying by 1 / 255
mul out_col, consts1.z, in_col