all repos — mgba @ 0c748d3bfc6b8865a92307243101909ff79c3f4b

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_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 256
 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 (x >= 400 && w >= 0) {
150		return;
151	}
152	if (y >= 240 && h >= 0) {
153		return;
154	}
155
156	if (ctrNumVerts + ctrVertStart == MAX_NUM_QUADS) {
157		ctrFlushBatch();
158		C3D_Flush();
159		ctrNumVerts = 0;
160		ctrVertStart = 0;
161	}
162
163	struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrVertStart + ctrNumVerts];
164	vtx->x = x;
165	vtx->y = y;
166	vtx->w = w;
167	vtx->h = h;
168	vtx->u = u;
169	vtx->v = v;
170	vtx->uw = uw;
171	vtx->vh = vh;
172	vtx->abgr = color;
173
174	++ctrNumVerts;
175}
176
177void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
178	ctrAddRectScaled(color, x, y, w, h, u, v, w, h);
179}
180
181void ctrFlushBatch(void) {
182	if (ctrNumVerts == 0) {
183		return;
184	}
185
186	C3D_BufInfo* bufInfo = C3D_GetBufInfo();
187	BufInfo_Init(bufInfo);
188	BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 3, 0x210);
189
190	GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts);
191	C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
192
193	ctrVertStart += ctrNumVerts;
194	ctrNumVerts = 0;
195}
196
197void ctrFinalize(void) {
198	ctrFlushBatch();
199	C3D_Flush();
200	ctrNumVerts = 0;
201	ctrVertStart = 0;
202}