From af03ad75be05379bbecbc326e849d26d25237652 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 13 Sep 2018 18:12:32 -0700 Subject: [PATCH] Switch: Initial mGUI port --- CMakeLists.txt | 8 +- include/mgba-util/socket.h | 8 +- include/mgba-util/threading.h | 8 +- res/font-new.png | Bin 0 -> 15466 bytes src/core/config.c | 4 +- src/platform/switch/CMakeLists.txt | 27 ++ src/platform/switch/CMakeToolchain.txt | 2 +- src/platform/switch/gui-font.c | 320 +++++++++++++++++++++++ src/platform/switch/icon.jpg | Bin 0 -> 20431 bytes src/platform/switch/main.c | 343 +++++++++++++++++++++++++ src/platform/test/perf-main.c | 8 +- src/util/gui/menu.c | 6 + 12 files changed, 718 insertions(+), 16 deletions(-) create mode 100644 res/font-new.png create mode 100644 src/platform/switch/gui-font.c create mode 100644 src/platform/switch/icon.jpg create mode 100644 src/platform/switch/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index bf824be50..f885052dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,8 @@ set(BUILD_PYTHON OFF CACHE BOOL "Build Python bindings") set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") -set(BUILD_GL ON CACHE STRING "Build with OpenGL") -set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") +set(BUILD_GL ON CACHE BOOL "Build with OpenGL") +set(BUILD_GLES2 OFF CACHE BOOL "Build with OpenGL|ES 2") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages") @@ -295,6 +295,10 @@ if(DEFINED 3DS) add_definitions(-DFIXED_ROM_BUFFER) endif() +if(DEFINED SWITCH) + set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2" FORCE) +endif() + if(NOT M_CORE_GBA) set(USE_GDB_STUB OFF) endif() diff --git a/include/mgba-util/socket.h b/include/mgba-util/socket.h index a2cece335..e0c52fb99 100644 --- a/include/mgba-util/socket.h +++ b/include/mgba-util/socket.h @@ -55,7 +55,7 @@ struct Address { extern u32* SOCUBuffer; #endif -#ifdef SWITCH +#ifdef __SWITCH__ #include #endif @@ -68,7 +68,7 @@ static inline void SocketSubsystemInit() { SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE); socInit(SOCUBuffer, SOCU_BUFFERSIZE); } -#elif defined(SWITCH) +#elif defined(__SWITCH__) socketInitializeDefault(); #endif } @@ -80,7 +80,7 @@ static inline void SocketSubsystemDeinit() { socExit(); free(SOCUBuffer); SOCUBuffer = NULL; -#elif defined(SWITCH) +#elif defined(__SWITCH__) socketExit(); #endif } @@ -110,7 +110,7 @@ static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) } static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) { -#if defined(_WIN32) || defined(SWITCH) +#if defined(_WIN32) || defined(__SWITCH__) return recv(socket, (char*) buffer, size, 0); #else return read(socket, buffer, size); diff --git a/include/mgba-util/threading.h b/include/mgba-util/threading.h index d59ebcc5a..a8b0b2176 100644 --- a/include/mgba-util/threading.h +++ b/include/mgba-util/threading.h @@ -13,12 +13,14 @@ CXX_GUARD_START #ifndef DISABLE_THREADING #ifdef USE_PTHREADS #include -#elif _WIN32 +#elif defined(_WIN32) #include -#elif PSP2 +#elif defined(PSP2) #include -#elif _3DS +#elif defined(_3DS) #include +#elif defined(__SWITCH__) +#include #else #define DISABLE_THREADING #endif diff --git a/res/font-new.png b/res/font-new.png new file mode 100644 index 0000000000000000000000000000000000000000..e08b5bccbd294cc63276c2f5ab35d2048f08dbae GIT binary patch literal 15466 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 Sv+LhY#L92q%U8ZJ5B*=TEv{_< literal 0 HcmV?d00001 diff --git a/src/core/config.c b/src/core/config.c index 44861e3c2..cb5c1a6dc 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -179,7 +179,7 @@ void mCoreConfigMakePortable(const struct mCoreConfig* config) { WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0); StringCchCatA(out, MAX_PATH, "\\portable.ini"); portable = VFileOpen(out, O_WRONLY | O_CREAT); -#elif defined(PSP2) || defined(_3DS) || defined(GEKKO) +#elif defined(PSP2) || defined(_3DS) || defined(__SWITCH__) || defined(GEKKO) // Already portable #else char out[PATH_MAX]; @@ -219,7 +219,7 @@ void mCoreConfigDirectory(char* out, size_t outLength) { UNUSED(portable); snprintf(out, outLength, "ux0:data/%s", projectName); sceIoMkdir(out, 0777); -#elif defined(GEKKO) +#elif defined(GEKKO) || defined(__SWITCH__) UNUSED(portable); snprintf(out, outLength, "/%s", projectName); mkdir(out, 0777); diff --git a/src/platform/switch/CMakeLists.txt b/src/platform/switch/CMakeLists.txt index 6b2e80679..14c591f07 100644 --- a/src/platform/switch/CMakeLists.txt +++ b/src/platform/switch/CMakeLists.txt @@ -1,12 +1,21 @@ find_program(ELF2NRO elf2nro) +find_program(NACPTOOL nacptool) +find_program(BUILD_ROMFS build_romfs) +find_library(GLAPI_LIBRARY glapi REQUIRED) +find_library(EGL_LIBRARY EGL REQUIRED) set(OS_DEFINES USE_VFS_FILE IOAPI_NO_64) list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) +list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c) + +include_directories(AFTER ${OPENGLES2_INCLUDE_DIR} ${OPENGL_EGL_INCLUDE_DIR}) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c) if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) + find_library(NOUVEAU_LIBRARY drm_nouveaud REQUIRED) list(APPEND OS_LIB nxd) else() + find_library(NOUVEAU_LIBRARY drm_nouveau REQUIRED) list(APPEND OS_LIB nx) endif() set(CORE_VFS_SRC ${CORE_VFS_SRC} PARENT_SCOPE) @@ -22,3 +31,21 @@ if(BUILD_PERF) ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.nro DESTINATION . COMPONENT ${BINARY_NAME}-perf) endif() + +add_executable(${BINARY_NAME}.elf ${GUI_SRC} ${PLATFORM_SRC} main.c) +set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") +target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${OS_LIB} ${EGL_LIBRARY} ${OPENGLES2_LIBRARY} ${GLAPI_LIBRARY} ${NOUVEAU_LIBRARY} stdc++) + +add_custom_command(OUTPUT control.nacp + COMMAND ${NACPTOOL} --create "${PROJECT_NAME}" "endrift" "${LIB_VERSION_STRING}" control.nacp) + +add_custom_command(OUTPUT romfs.bin + COMMAND ${CMAKE_COMMAND} -E make_directory romfs + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/res/font-new.png" romfs/ + COMMAND ${BUILD_ROMFS} romfs romfs.bin + COMMAND ${CMAKE_COMMAND} -E remove_directory romfs + DEPENDS "${CMAKE_SOURCE_DIR}/res/font-new.png") + +add_custom_target(${BINARY_NAME}.nro ALL + ${ELF2NRO} ${BINARY_NAME}.elf ${BINARY_NAME}.nro --romfs=romfs.bin --nacp=control.nacp --icon="${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg" + DEPENDS ${BINARY_NAME}.elf control.nacp ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg romfs.bin) diff --git a/src/platform/switch/CMakeToolchain.txt b/src/platform/switch/CMakeToolchain.txt index bf9db0101..3dbc1ad87 100644 --- a/src/platform/switch/CMakeToolchain.txt +++ b/src/platform/switch/CMakeToolchain.txt @@ -53,4 +53,4 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "") set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) set(SWITCH ON) -add_definitions(-DSWITCH) +add_definitions(-D__SWITCH__) diff --git a/src/platform/switch/gui-font.c b/src/platform/switch/gui-font.c new file mode 100644 index 000000000..382de6db0 --- /dev/null +++ b/src/platform/switch/gui-font.c @@ -0,0 +1,320 @@ +/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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 +#include +#include +#include +#include + +#include + +#define GLYPH_HEIGHT 24 +#define CELL_HEIGHT 32 +#define CELL_WIDTH 32 + +static const GLfloat _offsets[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, +}; + +static const GLchar* const _gles2Header = + "#version 100\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" + "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" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + "uniform vec4 color;\n" + "uniform float cutoff;\n" + + "void main() {\n" + " vec4 texColor = color;\n" + " texColor.a *= texture2D(tex, texCoord).a;\n" + " texColor.a = clamp((texColor.a - cutoff) / (1.0 - cutoff), 0.0, 1.0);\n" + " gl_FragColor = texColor;\n" + "}"; + +struct GUIFont { + GLuint font; + GLuint program; + GLuint vbo; + GLuint offsetLocation; + GLuint texLocation; + GLuint dimsLocation; + GLuint colorLocation; + GLuint originLocation; + GLuint glyphLocation; + GLuint cutoffLocation; +}; + +static bool _loadTexture(const char* path) { + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (!vf) { + return false; + } + png_structp png = PNGReadOpen(vf, 0); + png_infop info = png_create_info_struct(png); + png_infop end = png_create_info_struct(png); + bool success = false; + if (png && info && end) { + success = PNGReadHeader(png, info); + } + void* pixels = NULL; + if (success) { + unsigned height = png_get_image_height(png, info); + unsigned width = png_get_image_width(png, info); + pixels = malloc(width * height); + if (pixels) { + success = PNGReadPixels8(png, info, pixels, width, height, width); + success = success && PNGReadFooter(png, end); + } else { + success = false; + } + if (success) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); + } + } + PNGReadClose(png, info, end); + if (pixels) { + free(pixels); + } + vf->close(vf); + return success; +} + +struct GUIFont* GUIFontCreate(void) { + struct GUIFont* font = malloc(sizeof(struct GUIFont)); + if (!font) { + return NULL; + } + glGenTextures(1, &font->font); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, font->font); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (!_loadTexture("romfs:/font-new.png")) { + GUIFontDestroy(font); + return NULL; + } + + font->program = glCreateProgram(); + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const GLchar* shaderBuffer[2]; + + shaderBuffer[0] = _gles2Header; + + shaderBuffer[1] = _vertexShader; + glShaderSource(vertexShader, 2, shaderBuffer, NULL); + + shaderBuffer[1] = _fragmentShader; + glShaderSource(fragmentShader, 2, shaderBuffer, NULL); + + glAttachShader(font->program, vertexShader); + glAttachShader(font->program, fragmentShader); + + glCompileShader(fragmentShader); + + GLint success; + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg); + puts(msg); + } + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg); + puts(msg); + } + glLinkProgram(font->program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + font->texLocation = glGetUniformLocation(font->program, "tex"); + font->colorLocation = glGetUniformLocation(font->program, "color"); + font->dimsLocation = glGetUniformLocation(font->program, "dims"); + font->originLocation = glGetUniformLocation(font->program, "origin"); + font->glyphLocation = glGetUniformLocation(font->program, "glyph"); + font->cutoffLocation = glGetUniformLocation(font->program, "cutoff"); + font->offsetLocation = glGetAttribLocation(font->program, "offset"); + + glGenBuffers(1, &font->vbo); + glBindBuffer(GL_ARRAY_BUFFER, font->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return font; +} + +void GUIFontDestroy(struct GUIFont* font) { + glDeleteBuffers(1, &font->vbo); + glDeleteProgram(font->program); + glDeleteTextures(1, &font->font); + free(font); +} + +unsigned GUIFontHeight(const struct GUIFont* font) { + UNUSED(font); + return GLYPH_HEIGHT; +} + +unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) { + UNUSED(font); + if (glyph > 0x7F) { + glyph = '?'; + } + return defaultFontMetrics[glyph].width * 2; +} + +void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) { + UNUSED(font); + if (icon >= GUI_ICON_MAX) { + if (w) { + *w = 0; + } + if (h) { + *h = 0; + } + } else { + if (w) { + *w = defaultIconMetrics[icon].width * 2; + } + if (h) { + *h = defaultIconMetrics[icon].height * 2; + } + } +} + +void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) { + if (glyph > 0x7F) { + glyph = '?'; + } + struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph]; + + 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, (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); + + 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); +} + +void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) { + if (icon >= GUI_ICON_MAX) { + return; + } + struct GUIIconMetric metric = defaultIconMetrics[icon]; + switch (align & GUI_ALIGN_HCENTER) { + case GUI_ALIGN_HCENTER: + x -= metric.width; + break; + case GUI_ALIGN_RIGHT: + x -= metric.width * 2; + break; + } + switch (align & GUI_ALIGN_VCENTER) { + case GUI_ALIGN_VCENTER: + y -= metric.height; + break; + case GUI_ALIGN_BOTTOM: + y -= metric.height * 2; + break; + } + + glUseProgram(font->program); + switch (orient) { + case GUI_ORIENT_HMIRROR: + // TODO + break; + case GUI_ORIENT_VMIRROR: + // TODO + break; + case GUI_ORIENT_0: + default: + // TODO: Rotate + break; + } + + 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, y, 0); + + 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); +} + +void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) { + if (icon >= GUI_ICON_MAX) { + return; + } + struct GUIIconMetric metric = defaultIconMetrics[icon]; + // +} diff --git a/src/platform/switch/icon.jpg b/src/platform/switch/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..007c27d8cc50ac8e89f6f1bca7e4dcca4306c0cc GIT binary patch literal 20431 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 +#include +#include +#include + +#include +#include +#include + +#define AUTO_INPUT 0x4E585031 + +static EGLDisplay s_display; +static EGLContext s_context; +static EGLSurface s_surface; + +static const GLfloat _offsets[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, +}; + +static const GLchar* const _gles2Header = + "#version 100\n" + "precision mediump float;\n"; + +static const char* const _vertexShader = + "attribute vec2 offset;\n" + "uniform vec2 dims;\n" + "uniform vec2 insize;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " vec2 ratio = insize / 256.0;\n" + " vec2 scaledOffset = offset * dims;\n" + " gl_Position = vec4(scaledOffset.x * 2.0 - dims.x, scaledOffset.y * -2.0 + dims.y, 0.0, 1.0);\n" + " texCoord = offset * ratio;\n" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + "uniform vec4 color;\n" + + "void main() {\n" + " vec4 texColor = vec4(texture2D(tex, texCoord).rgb, 1.0);\n" + " texColor *= color;\n" + " gl_FragColor = texColor;\n" + "}"; + +static GLuint program; +static GLuint vbo; +static GLuint offsetLocation; +static GLuint texLocation; +static GLuint dimsLocation; +static GLuint insizeLocation; +static GLuint colorLocation; +static GLuint tex; + +static color_t frameBuffer[256 * 256]; + +static bool initEgl() { + s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!s_display) { + goto _fail0; + } + + eglInitialize(s_display, NULL, NULL); + + EGLConfig config; + EGLint numConfigs; + static const EGLint attributeList[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_NONE + }; + eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs); + if (!numConfigs) { + goto _fail1; + } + + s_surface = eglCreateWindowSurface(s_display, config, "", NULL); + if (!s_surface) { + goto _fail1; + } + + s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL); + if (!s_context) { + goto _fail2; + } + + eglMakeCurrent(s_display, s_surface, s_surface, s_context); + return true; + +_fail2: + eglDestroySurface(s_display, s_surface); + s_surface = NULL; +_fail1: + eglTerminate(s_display); + s_display = NULL; +_fail0: + return false; +} + +static void deinitEgl() { + if (s_display) { + if (s_context) { + eglDestroyContext(s_display, s_context); + } + if (s_surface) { + eglDestroySurface(s_display, s_surface); + } + eglTerminate(s_display); + } +} + +static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) { + mInputBindKey(map, binding, __builtin_ctz(nativeKey), key); +} + +static void _drawStart(void) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +static void _drawEnd(void) { + eglSwapBuffers(s_display, s_surface); +} + +static uint32_t _pollInput(const struct mInputMap* map) { + int keys = 0; + hidScanInput(); + u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); + keys |= mInputMapKeyBits(map, AUTO_INPUT, padkeys, 0); + return keys; +} + +static void _setup(struct mGUIRunner* runner) { + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_A, GBA_KEY_A); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_B, GBA_KEY_B); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_PLUS, GBA_KEY_START); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_MINUS, GBA_KEY_SELECT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DUP, GBA_KEY_UP); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DDOWN, GBA_KEY_DOWN); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DLEFT, GBA_KEY_LEFT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DRIGHT, GBA_KEY_RIGHT); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_L, GBA_KEY_L); + _mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R); + + runner->core->setVideoBuffer(runner->core, frameBuffer, 256); +} + +static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glUseProgram(program); + float aspectX = width / (float) runner->params.width; + float aspectY = height / (float) runner->params.height; + float max; + if (aspectX > aspectY) { + max = floor(1.0 / aspectX); + } else { + max = floor(1.0 / aspectY); + } + + aspectX *= max; + aspectY *= max; + + glUniform1i(texLocation, 0); + glUniform2f(dimsLocation, aspectX, aspectY); + glUniform2f(insizeLocation, width, height); + if (!faded) { + glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f); + } else { + glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f); + } + + glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glEnableVertexAttribArray(offsetLocation); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(offsetLocation); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); +} + +static void _drawFrame(struct mGUIRunner* runner, bool faded) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameBuffer); + + unsigned width, height; + runner->core->desiredVideoDimensions(runner->core, &width, &height); + _drawTex(runner, width, height, faded); +} + +static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + _drawTex(runner, width, height, faded); +} + +static uint16_t _pollGameInput(struct mGUIRunner* runner) { + int keys = 0; + hidScanInput(); + u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); + keys |= mInputMapKeyBits(&runner->core->inputMap, AUTO_INPUT, padkeys, 0); + return keys; +} + +static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { + UNUSED(runner); +} + +static bool _running(struct mGUIRunner* runner) { + UNUSED(runner); + return appletMainLoop(); +} + +int main(int argc, char* argv[]) { + socketInitializeDefault(); + nxlinkStdio(); + initEgl(); + romfsInit(); + + struct GUIFont* font = GUIFontCreate(); + + u32 width = 1280; + u32 height = 720; + + glViewport(0, 0, width, height); + glClearColor(0.f, 0.f, 0.f, 1.f); + + glGenTextures(1, &tex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + program = glCreateProgram(); + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const GLchar* shaderBuffer[2]; + + shaderBuffer[0] = _gles2Header; + + shaderBuffer[1] = _vertexShader; + glShaderSource(vertexShader, 2, shaderBuffer, NULL); + + shaderBuffer[1] = _fragmentShader; + glShaderSource(fragmentShader, 2, shaderBuffer, NULL); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + glCompileShader(fragmentShader); + + GLint success; + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg); + puts(msg); + } + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar msg[512]; + glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg); + puts(msg); + } + glLinkProgram(program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + texLocation = glGetUniformLocation(program, "tex"); + colorLocation = glGetUniformLocation(program, "color"); + dimsLocation = glGetUniformLocation(program, "dims"); + insizeLocation = glGetUniformLocation(program, "insize"); + offsetLocation = glGetAttribLocation(program, "offset"); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + struct mGUIRunner runner = { + .params = { + width, height, + font, "/", + _drawStart, _drawEnd, + _pollInput, NULL, + NULL, + NULL, NULL, + }, + .nConfigExtra = 0, + .setup = _setup, + .teardown = NULL, + .gameLoaded = NULL, + .gameUnloaded = NULL, + .prepareForFrame = NULL, + .drawFrame = _drawFrame, + .drawScreenshot = _drawScreenshot, + .paused = NULL, + .unpaused = NULL, + .incrementScreenMode = NULL, + .setFrameLimiter = _setFrameLimiter, + .pollGameInput = _pollGameInput, + .running = _running + }; + mGUIInit(&runner, "switch"); + + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_A, GUI_INPUT_SELECT); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_B, GUI_INPUT_BACK); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_X, GUI_INPUT_CANCEL); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DUP, GUI_INPUT_UP); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DDOWN, GUI_INPUT_DOWN); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DLEFT, GUI_INPUT_LEFT); + _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT); + + mGUIRunloop(&runner); + + deinitEgl(); + socketExit(); + return 0; +} diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index 29cad8e05..2f96057f2 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -19,7 +19,7 @@ #ifdef _3DS #include <3ds.h> #endif -#ifdef SWITCH +#ifdef __SWITCH__ #include #endif @@ -52,7 +52,7 @@ struct PerfOpts { extern bool allocateRomBuffer(void); FS_Archive sdmcArchive; #endif -#ifdef SWITCH +#ifdef __SWITCH__ TimeType __nx_time_type = TimeType_LocalSystemClock; #endif @@ -77,7 +77,7 @@ int main(int argc, char** argv) { if (!allocateRomBuffer()) { return 1; } -#elif defined(SWITCH) +#elif defined(__SWITCH__) UNUSED(_mPerfShutdown); gfxInitDefault(); consoleInit(NULL); @@ -138,7 +138,7 @@ int main(int argc, char** argv) { #ifdef _3DS gfxExit(); acExit(); -#elif defined(SWITCH) +#elif defined(__SWITCH__) gfxExit(); #endif diff --git a/src/util/gui/menu.c b/src/util/gui/menu.c index 6f8eb8327..763826c4b 100644 --- a/src/util/gui/menu.c +++ b/src/util/gui/menu.c @@ -10,6 +10,8 @@ #ifdef _3DS #include <3ds.h> +#elif defined(__SWITCH__) +#include #endif DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem); @@ -31,6 +33,10 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men if (!aptMainLoop()) { return GUI_MENU_EXIT_CANCEL; } +#elif defined(__SWITCH__) + if (!appletMainLoop()) { + return GUI_MENU_EXIT_CANCEL; + } #endif uint32_t newInput = 0; GUIPollInput(params, &newInput, 0);