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 2048
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
125 C3D_TexEnv* env = C3D_GetTexEnv(0);
126 C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
127 if (texture->fmt < GPU_LA8) {
128 C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
129 C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
130 } else {
131 C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR, 0, 0);
132 C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0);
133 C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
134 C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
135 }
136
137 C3D_Mtx textureMtx = {
138 .m = {
139 // Rows are in the order w z y x, because ctrulib
140 0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width,
141 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f
142 }
143 };
144 C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx);
145}
146
147void ctrAddRectScaled(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh) {
148 if (ctrNumVerts + ctrVertStart == MAX_NUM_QUADS) {
149 ctrFlushBatch();
150 C3D_Flush();
151 ctrNumVerts = 0;
152 ctrVertStart = 0;
153 }
154
155 struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrVertStart + ctrNumVerts];
156 vtx->x = x;
157 vtx->y = y;
158 vtx->w = w;
159 vtx->h = h;
160 vtx->u = u;
161 vtx->v = v;
162 vtx->uw = uw;
163 vtx->vh = vh;
164 vtx->abgr = color;
165
166 ++ctrNumVerts;
167}
168
169void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
170 ctrAddRectScaled(color, x, y, w, h, u, v, w, h);
171}
172
173void ctrFlushBatch(void) {
174 if (ctrNumVerts == 0) {
175 return;
176 }
177
178 C3D_TexBind(0, activeTexture);
179
180 C3D_BufInfo* bufInfo = C3D_GetBufInfo();
181 BufInfo_Init(bufInfo);
182 BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 3, 0x210);
183
184 GSPGPU_FlushDataCache(ctrVertexBuffer, VERTEX_BUFFER_SIZE);
185 C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
186
187 ctrVertStart += ctrNumVerts;
188 ctrNumVerts = 0;
189}