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.h"
18#include "uishader.shbin.h"
19
20struct ctrUIVertex {
21 short x, y;
22 short w, h;
23 short u, v;
24 short uw, vh;
25 u32 abgr;
26 float rotate[2];
27};
28
29#define MAX_NUM_QUADS 256
30#define BUFFER_PARTITIONS 4
31#define VERTEX_BUFFER_SIZE MAX_NUM_QUADS * BUFFER_PARTITIONS * sizeof(struct ctrUIVertex)
32
33static struct ctrUIVertex* ctrVertexBuffer = NULL;
34static int ctrNumVerts = 0;
35static int ctrVertStart = 0;
36static int ctrVertPartition = 0;
37
38static C3D_Tex* activeTexture = NULL;
39
40static shaderProgram_s uiProgram;
41static DVLB_s* uiShader = NULL;
42static int GSH_FVEC_projectionMtx;
43static int GSH_FVEC_textureMtx;
44
45bool ctrInitGpu() {
46 // Load vertex shader binary
47 uiShader = DVLB_ParseFile((u32*) uishader, uishader_size);
48 if (uiShader == NULL) {
49 return false;
50 }
51
52 // Create shader
53 shaderProgramInit(&uiProgram);
54 Result res = shaderProgramSetVsh(&uiProgram, &uiShader->DVLE[0]);
55 if (res < 0) {
56 return false;
57 }
58 res = shaderProgramSetGsh(&uiProgram, &uiShader->DVLE[1], 4);
59 if (res < 0) {
60 return false;
61 }
62 C3D_BindProgram(&uiProgram);
63 GSH_FVEC_projectionMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "projectionMtx");
64 GSH_FVEC_textureMtx = shaderInstanceGetUniformLocation(uiProgram.geometryShader, "textureMtx");
65
66 // Allocate buffers
67 ctrVertexBuffer = linearAlloc(VERTEX_BUFFER_SIZE);
68 if (ctrVertexBuffer == NULL) {
69 return false;
70 }
71
72 C3D_CullFace(GPU_CULL_NONE);
73 C3D_DepthTest(false, GPU_ALWAYS, GPU_WRITE_ALL);
74 C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
75 C3D_AlphaTest(false, GPU_ALWAYS, 0);
76 C3D_BlendingColor(0);
77
78 C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
79 AttrInfo_Init(attrInfo);
80 AttrInfo_AddLoader(attrInfo, 0, GPU_SHORT, 4); // in_pos
81 AttrInfo_AddLoader(attrInfo, 1, GPU_SHORT, 4); // in_tc0
82 AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
83 AttrInfo_AddLoader(attrInfo, 3, GPU_FLOAT, 2); // in_rot
84
85 return true;
86}
87
88void ctrDeinitGpu() {
89 if (ctrVertexBuffer) {
90 linearFree(ctrVertexBuffer);
91 ctrVertexBuffer = NULL;
92 }
93
94 shaderProgramFree(&uiProgram);
95
96 if (uiShader) {
97 DVLB_Free(uiShader);
98 uiShader = NULL;
99 }
100}
101
102void ctrSetViewportSize(s16 w, s16 h, bool tilt) {
103 C3D_SetViewport(0, 0, h, w);
104 C3D_Mtx projectionMtx;
105 if (tilt) {
106 Mtx_OrthoTilt(&projectionMtx, 0.0, w, h, 0.0, 0.0, 1.0, true);
107 } else {
108 Mtx_Ortho(&projectionMtx, 0.0, w, 0.0, h, 0.0, 1.0, true);
109 }
110 C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx);
111}
112
113void ctrActivateTexture(C3D_Tex* texture) {
114 if (texture == activeTexture) {
115 return;
116 }
117 if (activeTexture) {
118 ctrFlushBatch();
119 }
120
121 activeTexture = texture;
122 C3D_TexBind(0, activeTexture);
123
124 C3D_TexEnv* env = C3D_GetTexEnv(0);
125 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
126 if (texture->fmt < GPU_LA8) {
127 C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
128 C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
129 } else {
130 C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR, 0, 0);
131 C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
132 C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
133 C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
134 }
135 env = C3D_GetTexEnv(1);
136 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
137 C3D_TexEnvSrc(env, C3D_Both, GPU_PREVIOUS, 0, 0);
138 C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
139 env = C3D_GetTexEnv(2);
140 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
141 C3D_TexEnvSrc(env, C3D_Both, GPU_PREVIOUS, 0, 0);
142 C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
143
144 C3D_Mtx textureMtx = {
145 .m = {
146 // Rows are in the order w z y x, because ctrulib
147 0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width,
148 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f
149 }
150 };
151 C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx);
152}
153
154void ctrTextureMultiply(void) {
155 C3D_TexEnv* env = C3D_GetTexEnv(1);
156 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
157 C3D_TexEnvSrc(env, C3D_Both, GPU_PREVIOUS, GPU_TEXTURE0, 0);
158 C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
159}
160
161void ctrTextureBias(u32 color) {
162 C3D_TexEnv* env = C3D_GetTexEnv(2);
163 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
164 C3D_TexEnvSrc(env, C3D_Both, GPU_PREVIOUS, GPU_CONSTANT, 0);
165 C3D_TexEnvFunc(env, C3D_Both, GPU_ADD);
166 C3D_TexEnvColor(env, color);
167}
168
169void ctrAddRectEx(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh, float rotate) {
170 if (x >= 400 && w >= 0) {
171 return;
172 }
173 if (y >= 240 && h >= 0) {
174 return;
175 }
176
177 if (ctrNumVerts + ctrVertStart == MAX_NUM_QUADS) {
178 ctrFlushBatch();
179 ++ctrVertPartition;
180 if (ctrVertPartition == BUFFER_PARTITIONS) {
181 svcBreak(USERBREAK_PANIC);
182 }
183 ctrVertStart = ctrVertPartition * MAX_NUM_QUADS;
184 }
185
186 struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrVertStart + ctrNumVerts];
187 vtx->x = x;
188 vtx->y = y;
189 vtx->w = w;
190 vtx->h = h;
191 vtx->u = u;
192 vtx->v = v;
193 vtx->uw = uw;
194 vtx->vh = vh;
195 vtx->abgr = color;
196 vtx->rotate[0] = cosf(rotate);
197 vtx->rotate[1] = sinf(rotate);
198
199 ++ctrNumVerts;
200}
201
202void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
203 ctrAddRectEx(color, x, y, w, h, u, v, w, h, 0);
204}
205
206void ctrFlushBatch(void) {
207 if (ctrNumVerts == 0) {
208 return;
209 }
210
211 C3D_BufInfo* bufInfo = C3D_GetBufInfo();
212 BufInfo_Init(bufInfo);
213 BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 4, 0x3210);
214
215 GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts);
216 C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
217
218 ctrVertStart += ctrNumVerts;
219 ctrNumVerts = 0;
220}
221
222void ctrFinalize(void) {
223 ctrFlushBatch();
224 ctrVertStart = 0;
225 ctrVertPartition = 0;
226}