Switch: Optimize font rendering (fixes #2078)
Vicki Pfau vi@endrift.com
Wed, 24 Mar 2021 19:10:56 -0700
4 files changed,
218 insertions(+),
77 deletions(-)
M
CHANGES
→
CHANGES
@@ -153,6 +153,7 @@ - Qt: Add button to jump to log settings
- Qt: Use relative paths in portable mode when applicable (fixes mgba.io/i/838) - Qt: Better initial shortcut editor column sizes - SDL: Fall back to sw blit if OpenGL init fails + - Switch: Optimize font rendering (fixes mgba.io/i/2078) - Util: Reset vector size on deinit - VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)
M
include/mgba-util/gui/font.h
→
include/mgba-util/gui/font.h
@@ -81,12 +81,17 @@ unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph);
unsigned GUIFontSpanWidth(const struct GUIFont*, const char* text); void GUIFontIconMetrics(const struct GUIFont*, enum GUIIcon icon, unsigned* w, unsigned* h); +// TODO: de-const these ATTRIBUTE_FORMAT(printf, 6, 7) void GUIFontPrintf(const struct GUIFont*, int x, int y, enum GUIAlignment, uint32_t color, const char* text, ...); void GUIFontPrint(const struct GUIFont*, int x, int y, enum GUIAlignment, uint32_t color, const char* text); void GUIFontDrawGlyph(const struct GUIFont*, int x, int y, uint32_t color, uint32_t glyph); void GUIFontDrawIcon(const struct GUIFont*, int x, int y, enum GUIAlignment, enum GUIOrientation, uint32_t color, enum GUIIcon); void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon); + +#ifdef __SWITCH__ +void GUIFontDrawSubmit(struct GUIFont* font); +#endif CXX_GUARD_END
M
src/platform/switch/gui-font.c
→
src/platform/switch/gui-font.c
@@ -14,6 +14,7 @@
#define GLYPH_HEIGHT 24 #define CELL_HEIGHT 32 #define CELL_WIDTH 32 +#define MAX_GLYPHS 1024 static const GLfloat _offsets[] = { 0.f, 0.f,@@ -22,50 +23,70 @@ 1.f, 1.f,
0.f, 1.f, }; -static const GLchar* const _gles2Header = - "#version 100\n" +static const GLchar* const _gles3Header = + "#version 300 es\n" "precision mediump float;\n"; static const char* const _vertexShader = - "attribute vec2 offset;\n" - "uniform vec3 origin;\n" - "uniform vec2 glyph;\n" - "uniform vec2 dims;\n" - "uniform mat2 transform;\n" - "varying vec2 texCoord;\n" + "in vec2 offset;\n" + "in vec3 origin;\n" + "in vec2 glyph;\n" + "in vec2 dims;\n" + "in mat2 transform;\n" + "in vec4 color;\n" + "out vec4 fragColor;\n" + "out vec2 texCoord;\n" "void main() {\n" " texCoord = (glyph + offset * dims) / 512.0;\n" " vec2 scaledOffset = (transform * (offset * 2.0 - vec2(1.0)) + vec2(1.0)) / 2.0 * dims;\n" + " fragColor = color;\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 = - "varying vec2 texCoord;\n" + "in vec2 texCoord;\n" + "in vec4 fragColor;\n" + "out vec4 outColor;\n" "uniform sampler2D tex;\n" - "uniform vec4 color;\n" "uniform float cutoff;\n" + "uniform vec3 colorModulus;\n" "void main() {\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" + " texColor.rgb = fragColor.rgb * colorModulus;\n" + " texColor.a *= fragColor.a;\n" + " outColor = texColor;\n" "}"; struct GUIFont { GLuint font; + int currentGlyph; GLuint program; GLuint vbo; GLuint vao; GLuint texLocation; - GLuint dimsLocation; - GLuint transformLocation; - GLuint colorLocation; + GLuint cutoffLocation; + GLuint colorModulusLocation; + GLuint originLocation; GLuint glyphLocation; - GLuint cutoffLocation; + GLuint dimsLocation; + GLuint transformLocation[2]; + GLuint colorLocation; + + GLuint originVbo; + GLuint glyphVbo; + GLuint dimsVbo; + GLuint transformVbo[2]; + GLuint colorVbo; + + GLfloat originData[MAX_GLYPHS][3]; + GLfloat glyphData[MAX_GLYPHS][2]; + GLfloat dimsData[MAX_GLYPHS][2]; + GLfloat transformData[2][MAX_GLYPHS][2]; + GLfloat colorData[MAX_GLYPHS][4]; }; static bool _loadTexture(const char* path) {@@ -120,12 +141,13 @@ GUIFontDestroy(font);
return NULL; } + font->currentGlyph = 0; font->program = glCreateProgram(); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); const GLchar* shaderBuffer[2]; - shaderBuffer[0] = _gles2Header; + shaderBuffer[0] = _gles3Header; shaderBuffer[1] = _vertexShader; glShaderSource(vertexShader, 2, shaderBuffer, NULL);@@ -154,27 +176,82 @@ GLchar msg[512];
glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg); puts(msg); } + glLinkProgram(font->program); + glGetProgramiv(font->program, GL_LINK_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetProgramInfoLog(font->program, sizeof(msg), NULL, msg); + puts(msg); + } + glDeleteShader(vertexShader); glDeleteShader(fragmentShader); 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"); + font->colorModulusLocation = glGetUniformLocation(font->program, "colorModulus"); + + font->originLocation = glGetAttribLocation(font->program, "origin"); + font->glyphLocation = glGetAttribLocation(font->program, "glyph"); + font->dimsLocation = glGetAttribLocation(font->program, "dims"); + font->transformLocation[0] = glGetAttribLocation(font->program, "transform"); + font->transformLocation[1] = font->transformLocation[0] + 1; + font->colorLocation = glGetAttribLocation(font->program, "color"); + GLuint offsetLocation = glGetAttribLocation(font->program, "offset"); - glGenBuffers(1, &font->vbo); glGenVertexArrays(1, &font->vao); glBindVertexArray(font->vao); + + glGenBuffers(1, &font->vbo); glBindBuffer(GL_ARRAY_BUFFER, font->vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(offsetLocation, 0); glEnableVertexAttribArray(offsetLocation); + + glGenBuffers(1, &font->originVbo); + glBindBuffer(GL_ARRAY_BUFFER, font->originVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->originLocation, 3, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->originLocation, 1); + glEnableVertexAttribArray(font->originLocation); + + glGenBuffers(1, &font->glyphVbo); + glBindBuffer(GL_ARRAY_BUFFER, font->glyphVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->glyphLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->glyphLocation, 1); + glEnableVertexAttribArray(font->glyphLocation); + + glGenBuffers(1, &font->dimsVbo); + glBindBuffer(GL_ARRAY_BUFFER, font->dimsVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->dimsLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->dimsLocation, 1); + glEnableVertexAttribArray(font->dimsLocation); + + glGenBuffers(2, font->transformVbo); + glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->transformLocation[0], 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->transformLocation[0], 1); + glEnableVertexAttribArray(font->transformLocation[0]); + glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->transformLocation[1], 2, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->transformLocation[1], 1); + glEnableVertexAttribArray(font->transformLocation[1]); + + glGenBuffers(1, &font->colorVbo); + glBindBuffer(GL_ARRAY_BUFFER, font->colorVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glVertexAttribPointer(font->colorLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL); + glVertexAttribDivisor(font->colorLocation, 1); + glEnableVertexAttribArray(font->colorLocation); + glBindVertexArray(0); return font;@@ -182,6 +259,11 @@ }
void GUIFontDestroy(struct GUIFont* font) { glDeleteBuffers(1, &font->vbo); + glDeleteBuffers(1, &font->originVbo); + glDeleteBuffers(1, &font->glyphVbo); + glDeleteBuffers(1, &font->dimsVbo); + glDeleteBuffers(2, font->transformVbo); + glDeleteBuffers(1, &font->colorVbo); glDeleteProgram(font->program); glDeleteTextures(1, &font->font); glDeleteVertexArrays(1, &font->vao);@@ -226,30 +308,30 @@ glyph = '?';
} struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph]; - glUseProgram(font->program); - glBindVertexArray(font->vao); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, font->font); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glUniform1i(font->texLocation, 0); - 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}); + struct GUIFont* mutfont = (struct GUIFont*) font; + if (font->currentGlyph >= MAX_GLYPHS) { + GUIFontDrawSubmit(mutfont); + } - 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); + int offset = font->currentGlyph; - glUniform1f(font->cutoffLocation, 0.7f); - glUniform4f(font->colorLocation, (color & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + mutfont->originData[offset][0] = x; + mutfont->originData[offset][1] = y - GLYPH_HEIGHT + metric.padding.top * 2; + mutfont->originData[offset][2] = 0; + mutfont->glyphData[offset][0] = (glyph & 15) * CELL_WIDTH + metric.padding.left * 2; + mutfont->glyphData[offset][1] = (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2; + mutfont->dimsData[offset][0] = CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2; + mutfont->dimsData[offset][1] = CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2; + mutfont->transformData[0][offset][0] = 1.0f; + mutfont->transformData[0][offset][1] = 0.0f; + mutfont->transformData[1][offset][0] = 0.0f; + mutfont->transformData[1][offset][1] = 1.0f; + mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f; + mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f; + mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f; + mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f; - glBindVertexArray(0); - glUseProgram(0); + ++mutfont->currentGlyph; } void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {@@ -277,7 +359,6 @@ y -= metric.height * 2;
break; } - glUseProgram(font->program); switch (orient) { case GUI_ORIENT_HMIRROR: hFlip = -1.0;@@ -291,30 +372,30 @@ // TODO: Rotate
break; } - glUseProgram(font->program); - glBindVertexArray(font->vao); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, font->font); + struct GUIFont* mutfont = (struct GUIFont*) font; + if (font->currentGlyph >= MAX_GLYPHS) { + GUIFontDrawSubmit(mutfont); + } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int offset = font->currentGlyph; - 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, y, 0); - glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip}); - - 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 & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + mutfont->originData[offset][0] = x; + mutfont->originData[offset][1] = y; + mutfont->originData[offset][2] = 0; + mutfont->glyphData[offset][0] = metric.x * 2; + mutfont->glyphData[offset][1] = metric.y * 2 + 256; + mutfont->dimsData[offset][0] = metric.width * 2; + mutfont->dimsData[offset][1] = metric.height * 2; + mutfont->transformData[0][offset][0] = hFlip; + mutfont->transformData[0][offset][1] = 0.0f; + mutfont->transformData[1][offset][0] = 0.0f; + mutfont->transformData[1][offset][1] = vFlip; + mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f; + mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f; + mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f; + mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f; - glBindVertexArray(0); - glUseProgram(0); + ++mutfont->currentGlyph; } void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {@@ -330,6 +411,33 @@ if (!h) {
h = metric.height * 2; } + struct GUIFont* mutfont = (struct GUIFont*) font; + if (font->currentGlyph >= MAX_GLYPHS) { + GUIFontDrawSubmit(mutfont); + } + + int offset = font->currentGlyph; + + mutfont->originData[offset][0] = x + w / 2 - metric.width; + mutfont->originData[offset][1] = y + h / 2 - metric.height; + mutfont->originData[offset][2] = 0; + mutfont->glyphData[offset][0] = metric.x * 2; + mutfont->glyphData[offset][1] = metric.y * 2 + 256; + mutfont->dimsData[offset][0] = metric.width * 2; + mutfont->dimsData[offset][1] = metric.height * 2; + mutfont->transformData[0][offset][0] = w * 0.5f / metric.width; + mutfont->transformData[0][offset][1] = 0.0f; + mutfont->transformData[1][offset][0] = 0.0f; + mutfont->transformData[1][offset][1] = h * 0.5f / metric.height; + mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f; + mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f; + mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f; + mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f; + + ++mutfont->currentGlyph; +} + +void GUIFontDrawSubmit(struct GUIFont* font) { glUseProgram(font->program); glBindVertexArray(font->vao); glActiveTexture(GL_TEXTURE0);@@ -339,18 +447,40 @@ 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}); + + glBindBuffer(GL_ARRAY_BUFFER, font->originVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 3 * font->currentGlyph, font->originData); + + glBindBuffer(GL_ARRAY_BUFFER, font->glyphVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->glyphData); + + glBindBuffer(GL_ARRAY_BUFFER, font->dimsVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->dimsData); + + glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->transformData[0]); + + glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->transformData[1]); + + glBindBuffer(GL_ARRAY_BUFFER, font->colorVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * MAX_GLYPHS, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 4 * font->currentGlyph, font->colorData); 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); + glUniform3f(font->colorModulusLocation, 0.f, 0.f, 0.f); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, font->currentGlyph); 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); + glUniform3f(font->colorModulusLocation, 1.f, 1.f, 1.f); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, font->currentGlyph); + + font->currentGlyph = 0; glBindVertexArray(0); glUseProgram(0);
M
src/platform/switch/main.c
→
src/platform/switch/main.c
@@ -78,6 +78,7 @@ static GLuint colorLocation;
static GLuint tex; static GLuint oldTex; +static struct GUIFont* font; static color_t* frameBuffer; static struct mAVStream stream; static struct mSwitchRumble {@@ -640,6 +641,10 @@ static void _guiPrepare(void) {
glViewport(0, 1080 - vheight, vwidth, vheight); } +static void _guiFinish(void) { + GUIFontDrawSubmit(font); +} + int main(int argc, char* argv[]) { NWindow* window = nwindowGetDefault(); nwindowSetDimensions(window, 1920, 1080);@@ -651,7 +656,7 @@ romfsInit();
audoutInitialize(); psmInitialize(); - struct GUIFont* font = GUIFontCreate(); + font = GUIFontCreate(); vmode = appletGetOperationMode(); if (vmode == AppletOperationMode_Console) {@@ -802,7 +807,7 @@ font, "/",
_drawStart, _drawEnd, _pollInput, _pollCursor, _batteryState, - _guiPrepare, NULL, + _guiPrepare, _guiFinish, }, .keySources = (struct GUIInputKeys[]) { {