all repos — mgba @ a14126c9775379eb52f8b9c1add0ca96781f2f05

mGBA Game Boy Advance Emulator

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}