src/platform/3ds/ctr-gpu.c (view raw)
1/* Copyright (c) 2015 Yuri Kunde Schlesner
2 * Copyright (c) 2016 Jeffrey Pfau
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8#include <3ds.h>
9#include <3ds/gpu/gpu.h>
10#include <3ds/gpu/gx.h>
11#include <stdlib.h>
12#include <string.h>
13#include <stdio.h>
14
15#include "ctr-gpu.h"
16
17#include "uishader_v.h"
18#include "uishader_v.shbin.h"
19#include "uishader_g.h"
20#include "uishader_g.shbin.h"
21
22struct ctrUIVertex {
23 short x, y;
24 short w, h;
25 short u, v;
26 short uw, vh;
27 u32 abgr;
28};
29
30#define MAX_NUM_QUADS 1024
31#define VERTEX_BUFFER_SIZE MAX_NUM_QUADS * sizeof(struct ctrUIVertex)
32
33static struct ctrUIVertex* ctrVertexBuffer = NULL;
34static int ctrNumVerts = 0;
35static int ctrVertStart = 0;
36
37static C3D_Tex* activeTexture = NULL;
38
39static shaderProgram_s gpuShader;
40static DVLB_s* vertexShader = NULL;
41static DVLB_s* geometryShader = NULL;
42
43bool ctrInitGpu() {
44 // Load vertex shader binary
45 vertexShader = DVLB_ParseFile((u32*) uishader_v, uishader_v_size);
46 if (vertexShader == NULL) {
47 return false;
48 }
49
50 // Load geometry shader binary
51 geometryShader = DVLB_ParseFile((u32*) uishader_g, uishader_g_size);
52 if (geometryShader == NULL) {
53 return false;
54 }
55
56 // Create shader
57 shaderProgramInit(&gpuShader);
58 Result res = shaderProgramSetVsh(&gpuShader, &vertexShader->DVLE[0]);
59 if (res < 0) {
60 return false;
61 }
62 res = shaderProgramSetGsh(&gpuShader, &geometryShader->DVLE[0], 3);
63 if (res < 0) {
64 return false;
65 }
66 C3D_BindProgram(&gpuShader);
67
68 // Allocate buffers
69 ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE);
70 if (ctrVertexBuffer == NULL) {
71 return false;
72 }
73
74 C3D_CullFace(GPU_CULL_NONE);
75 C3D_DepthTest(false, GPU_ALWAYS, GPU_WRITE_ALL);
76 C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
77 C3D_AlphaTest(false, GPU_ALWAYS, 0);
78 C3D_BlendingColor(0);
79
80 C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
81 AttrInfo_Init(attrInfo);
82 AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos
83 AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0
84 AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
85
86 return true;
87}
88
89void ctrDeinitGpu() {
90 if (ctrVertexBuffer) {
91 linearFree(ctrVertexBuffer);
92 ctrVertexBuffer = NULL;
93 }
94
95 shaderProgramFree(&gpuShader);
96
97 if (vertexShader) {
98 DVLB_Free(vertexShader);
99 vertexShader = NULL;
100 }
101
102 if (geometryShader) {
103 DVLB_Free(geometryShader);
104 geometryShader = NULL;
105 }
106}
107
108void ctrSetViewportSize(s16 w, s16 h) {
109 C3D_SetViewport(0, 0, h, w);
110 C3D_Mtx projectionMtx;
111 Mtx_OrthoTilt(&projectionMtx, 0.0, w, h, 0.0, 0.0, 1.0);
112 C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx);
113}
114
115void ctrActivateTexture(C3D_Tex* texture) {
116 if (texture == activeTexture) {
117 return;
118 }
119 if (activeTexture) {
120 ctrFlushBatch();
121 }
122
123 activeTexture = texture;
124 C3D_TexBind(0, activeTexture);
125
126 C3D_TexEnv* env = C3D_GetTexEnv(0);
127 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
128 if (texture->fmt < GPU_LA8) {
129 C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
130 C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
131 } else {
132 C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR, 0, 0);
133 C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
134 C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
135 C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
136 }
137
138 C3D_Mtx textureMtx = {
139 .m = {
140 // Rows are in the order w z y x, because ctrulib
141 0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width,
142 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f
143 }
144 };
145 C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx);
146}
147
148void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh) {
149 if (ctrNumVerts + ctrVertStart == MAX_NUM_QUADS) {
150 ctrFlushBatch();
151 C3D_Flush();
152 ctrNumVerts = 0;
153 ctrVertStart = 0;
154 }
155
156 struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrVertStart + ctrNumVerts];
157 vtx->x = x;
158 vtx->y = y;
159 vtx->w = w;
160 vtx->h = h;
161 vtx->u = u;
162 vtx->v = v;
163 vtx->uw = uw;
164 vtx->vh = vh;
165 vtx->abgr = color;
166
167 ++ctrNumVerts;
168}
169
170void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
171 ctrAddRectScaled(color, x, y, w, h, u, v, w, h);
172}
173
174void ctrFlushBatch(void) {
175 if (ctrNumVerts == 0) {
176 return;
177 }
178
179 C3D_BufInfo* bufInfo = C3D_GetBufInfo();
180 BufInfo_Init(bufInfo);
181 BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 3, 0x210);
182
183 GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts);
184 C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
185
186 ctrVertStart += ctrNumVerts;
187 ctrNumVerts = 0;
188}
189
190void ctrFinalize(void) {
191 ctrFlushBatch();
192 C3D_Flush();
193 ctrNumVerts = 0;
194 ctrVertStart = 0;
195}