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