Switch: Add audio, key mapping, fast-forward, bugfixes

This commit is contained in:
Vicki Pfau 2018-09-14 20:21:12 -07:00
parent af03ad75be
commit 21626502bb
4 changed files with 168 additions and 15 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -31,12 +31,13 @@ static const char* const _vertexShader =
"uniform vec3 origin;\n"
"uniform vec2 glyph;\n"
"uniform vec2 dims;\n"
"uniform mat2 transform;\n"
"varying vec2 texCoord;\n"
"void main() {\n"
" vec2 scaledOffset = offset * dims;\n"
" gl_Position = vec4((origin.x + scaledOffset.x) / 640.0 - 1.0, -(origin.y + scaledOffset.y) / 384.0 + 1.0, origin.z, 1.0);\n"
" texCoord = (glyph + scaledOffset) / 512.0;\n"
" texCoord = (glyph + offset * dims) / 512.0;\n"
" vec2 scaledOffset = (transform * (offset * 2.0 - vec2(1.0)) + vec2(1.0)) / 2.0 * dims;\n"
" gl_Position = vec4((origin.x + scaledOffset.x) / 640.0 - 1.0, -(origin.y + scaledOffset.y) / 360.0 + 1.0, origin.z, 1.0);\n"
"}";
static const char* const _fragmentShader =
@ -46,9 +47,10 @@ static const char* const _fragmentShader =
"uniform float cutoff;\n"
"void main() {\n"
" vec4 texColor = color;\n"
" texColor.a *= texture2D(tex, texCoord).a;\n"
" vec4 texColor = texture2D(tex, texCoord);\n"
" texColor.a = clamp((texColor.a - cutoff) / (1.0 - cutoff), 0.0, 1.0);\n"
" texColor.rgb = color.rgb;\n"
" texColor.a *= color.a;\n"
" gl_FragColor = texColor;\n"
"}";
@ -59,6 +61,7 @@ struct GUIFont {
GLuint offsetLocation;
GLuint texLocation;
GLuint dimsLocation;
GLuint transformLocation;
GLuint colorLocation;
GLuint originLocation;
GLuint glyphLocation;
@ -159,6 +162,7 @@ struct GUIFont* GUIFontCreate(void) {
font->texLocation = glGetUniformLocation(font->program, "tex");
font->colorLocation = glGetUniformLocation(font->program, "color");
font->dimsLocation = glGetUniformLocation(font->program, "dims");
font->transformLocation = glGetUniformLocation(font->program, "transform");
font->originLocation = glGetUniformLocation(font->program, "origin");
font->glyphLocation = glGetUniformLocation(font->program, "glyph");
font->cutoffLocation = glGetUniformLocation(font->program, "cutoff");
@ -229,6 +233,7 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color,
glUniform2f(font->glyphLocation, (glyph & 15) * CELL_WIDTH + metric.padding.left * 2, (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2);
glUniform2f(font->dimsLocation, CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2, CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2);
glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {1.0, 0.0, 0.0, 1.0});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
@ -251,6 +256,9 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
return;
}
struct GUIIconMetric metric = defaultIconMetrics[icon];
float hFlip = 1.0f;
float vFlip = 1.0f;
switch (align & GUI_ALIGN_HCENTER) {
case GUI_ALIGN_HCENTER:
x -= metric.width;
@ -271,10 +279,10 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
glUseProgram(font->program);
switch (orient) {
case GUI_ORIENT_HMIRROR:
// TODO
hFlip = -1.0;
break;
case GUI_ORIENT_VMIRROR:
// TODO
vFlip = -1.0;
break;
case GUI_ORIENT_0:
default:
@ -294,6 +302,7 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
glUniform3f(font->originLocation, x, y, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
@ -316,5 +325,40 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
return;
}
struct GUIIconMetric metric = defaultIconMetrics[icon];
//
if (!w) {
w = metric.width * 2;
}
if (!h) {
h = metric.height * 2;
}
glUseProgram(font->program);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font->font);
glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUniform1i(font->texLocation, 0);
glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
glUniform3f(font->originLocation, x + w / 2 - metric.width, y + h / 2 - metric.height, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {w * 0.5f / metric.width, 0.0, 0.0, h * 0.5f / metric.height});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
glUniform1f(font->cutoffLocation, 0.1f);
glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glUniform1f(font->cutoffLocation, 0.7f);
glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(font->offsetLocation);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
}

View File

@ -4,7 +4,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "feature/gui/gui-runner.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h>
#include <mgba/internal/gba/audio.h>
#include <mgba/internal/gba/input.h>
#include <mgba-util/gui.h>
#include <mgba-util/gui/font.h>
@ -14,6 +16,11 @@
#include <GLES2/gl2.h>
#define AUTO_INPUT 0x4E585031
#define SAMPLES 0x400
#define BUFFER_SIZE 0x1000
#define N_BUFFERS 3
TimeType __nx_time_type = TimeType_UserSystemClock;
static EGLDisplay s_display;
static EGLContext s_context;
@ -64,6 +71,13 @@ static GLuint colorLocation;
static GLuint tex;
static color_t frameBuffer[256 * 256];
static struct mAVStream stream;
static int audioBufferActive;
static struct GBAStereoSample audioBuffer[N_BUFFERS][SAMPLES] __attribute__((__aligned__(0x1000)));
static AudioOutBuffer audoutBuffer[N_BUFFERS];
static int enqueuedBuffers;
static bool frameLimiter = true;
static int framecount = 0;
static bool initEgl() {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@ -91,6 +105,10 @@ static bool initEgl() {
goto _fail1;
}
//EGLint contextAttributeList[] = {
// EGL_CONTEXT_CLIENT_VERSION, 2,
// EGL_NONE
//};
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL);
if (!s_context) {
goto _fail2;
@ -130,8 +148,10 @@ static void _drawStart(void) {
}
static void _drawEnd(void) {
if (frameLimiter || (framecount & 2) == 0) {
eglSwapBuffers(s_display, s_surface);
}
}
static uint32_t _pollInput(const struct mInputMap* map) {
int keys = 0;
@ -154,6 +174,15 @@ static void _setup(struct mGUIRunner* runner) {
_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R);
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
runner->core->setAVStream(runner->core, &stream);
}
static void _gameLoaded(struct mGUIRunner* runner) {
u32 samplerate = audoutGetSampleRate();
double ratio = GBAAudioCalculateRatio(1, 60.0, 1);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate * ratio);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate * ratio);
}
static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) {
@ -202,6 +231,8 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
_drawTex(runner, width, height, faded);
++framecount;
}
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
@ -222,6 +253,7 @@ static uint16_t _pollGameInput(struct mGUIRunner* runner) {
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
UNUSED(runner);
frameLimiter = limit;
}
static bool _running(struct mGUIRunner* runner) {
@ -229,11 +261,33 @@ static bool _running(struct mGUIRunner* runner) {
return appletMainLoop();
}
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
UNUSED(stream);
static AudioOutBuffer* releasedBuffers;
u32 audoutNReleasedBuffers;
audoutGetReleasedAudioOutBuffer(&releasedBuffers, &audoutNReleasedBuffers);
enqueuedBuffers -= audoutNReleasedBuffers;
if (!frameLimiter && enqueuedBuffers == N_BUFFERS) {
blip_clear(left);
blip_clear(right);
return;
}
struct GBAStereoSample* samples = audioBuffer[audioBufferActive];
blip_read_samples(left, &samples[0].left, SAMPLES, true);
blip_read_samples(right, &samples[0].right, SAMPLES, true);
audoutAppendAudioOutBuffer(&audoutBuffer[audioBufferActive]);
audioBufferActive += 1;
audioBufferActive %= N_BUFFERS;
++enqueuedBuffers;
}
int main(int argc, char* argv[]) {
socketInitializeDefault();
nxlinkStdio();
initEgl();
romfsInit();
audoutInitialize();
struct GUIFont* font = GUIFontCreate();
@ -301,6 +355,23 @@ int main(int argc, char* argv[]) {
glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
stream.videoDimensionsChanged = NULL;
stream.postVideoFrame = NULL;
stream.postAudioFrame = NULL;
stream.postAudioBuffer = _postAudioBuffer;
memset(audioBuffer, 0, sizeof(audioBuffer));
audioBufferActive = 0;
enqueuedBuffers = 0;
size_t i;
for (i = 0; i < N_BUFFERS; ++i) {
audoutBuffer[i].next = NULL;
audoutBuffer[i].buffer = audioBuffer[i];
audoutBuffer[i].buffer_size = BUFFER_SIZE;
audoutBuffer[i].data_size = BUFFER_SIZE;
audoutBuffer[i].data_offset = 0;
}
struct mGUIRunner runner = {
.params = {
width, height,
@ -310,16 +381,52 @@ int main(int argc, char* argv[]) {
NULL,
NULL, NULL,
},
.keySources = (struct GUIInputKeys[]) {
{
.name = "Controller Input",
.id = AUTO_INPUT,
.keyNames = (const char*[]) {
"A",
"B",
"X",
"Y",
"L Stick",
"R Stick",
"L",
"R",
"ZL",
"ZR",
"+",
"-",
"Left",
"Up",
"Right",
"Down",
"L Left",
"L Up",
"L Right",
"L Down",
"R Left",
"R Up",
"R Right",
"R Down",
"SL",
"SR"
},
.nKeys = 26
},
{ .id = 0 }
},
.nConfigExtra = 0,
.setup = _setup,
.teardown = NULL,
.gameLoaded = NULL,
.gameLoaded = _gameLoaded,
.gameUnloaded = NULL,
.prepareForFrame = NULL,
.drawFrame = _drawFrame,
.drawScreenshot = _drawScreenshot,
.paused = NULL,
.unpaused = NULL,
.unpaused = _gameLoaded,
.incrementScreenMode = NULL,
.setFrameLimiter = _setFrameLimiter,
.pollGameInput = _pollGameInput,
@ -335,8 +442,10 @@ int main(int argc, char* argv[]) {
_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DLEFT, GUI_INPUT_LEFT);
_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT);
audoutStartAudioOut();
mGUIRunloop(&runner);
audoutExit();
deinitEgl();
socketExit();
return 0;

View File

@ -176,8 +176,8 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_BUTTON, &right, 0);
GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_TRACK, &w, 0);
right = (right - w) / 2;
GUIFontDrawIconSize(params->font, params->width - right - 8, top, 0, bottom - top, 0xA0FFFFFF, GUI_ICON_SCROLLBAR_TRACK);
GUIFontDrawIcon(params->font, params->width - 8, top, GUI_ALIGN_HCENTER | GUI_ALIGN_BOTTOM, GUI_ORIENT_VMIRROR, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
GUIFontDrawIconSize(params->font, params->width - right - 8, top, 0, bottom - top, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_TRACK);
GUIFontDrawIcon(params->font, params->width - 8, bottom, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
y = menu->index * (bottom - top - 16) / GUIMenuItemListSize(&menu->items);