Path: blob/master/RSDKv5/RSDK/Graphics/EGL/EGLRenderDevice.cpp
1163 views
#ifdef _INTELLISENSE1#ifdef _INTELLISENSE_NX2#undef __unix__3#undef __linux__4#endif5#include <glad/glad.h>6#include "EGLRenderDevice.hpp"7//#include "RetroEngine.hpp"8#endif910#include <chrono>1112#if RETRO_PLATFORM == RETRO_SWITCH13#define _GLVERSION "#version 330 core\n#define in_V in\n#define in_F in\n"1415#define _glVPrecision ""16#define _glFPrecision ""1718#define _YOFF 1619#define _UOFF 820#define _VOFF 021#elif RETRO_PLATFORM == RETRO_ANDROID22#define _GLVERSION \23"#version 100\n#extension GL_OES_standard_derivatives : enable\n#define in_V attribute\n#define out varying\n#define in_F varying\n"2425#define GL_BGRA GL_RGBA26#define GL_UNSIGNED_INT_8_8_8_8_REV GL_UNSIGNED_BYTE2728char _glVPrecision[30]; // len("precision mediump float;\n") -> 2529char _glFPrecision[30]; // len("precision mediump float;\n") -> 253031#define _YOFF 032#define _UOFF 833#define _VOFF 1634#endif3536#if RETRO_REV0237#define _GLDEFINE "#define RETRO_REV02 (1)\n"38#else39#define _GLDEFINE "\n"40#endif4142const GLchar *backupVertex = R"aa(43in_V vec3 in_pos;44in_V vec2 in_UV;45out vec4 ex_color;46out vec2 ex_UV;4748void main()49{50gl_Position = vec4(in_pos, 1.0);51ex_color = vec4(1.0, 1.0, 1.0, 1.0);52ex_UV = in_UV;53}54)aa";5556const GLchar *backupFragment = R"aa(57in_F vec2 ex_UV;58in_F vec4 ex_color;5960uniform sampler2D texDiffuse;6162void main()63{64gl_FragColor = texture2D(texDiffuse, ex_UV);65}66)aa";6768EGLDisplay RenderDevice::display;69EGLContext RenderDevice::context;70EGLSurface RenderDevice::surface;71EGLConfig RenderDevice::config;7273#if RETRO_PLATFORM == RETRO_SWITCH74NWindow *RenderDevice::window;75#elif RETRO_PLATFORM == RETRO_ANDROID76ANativeWindow *RenderDevice::window;77#endif7879GLuint RenderDevice::VAO;80GLuint RenderDevice::VBO;8182GLuint RenderDevice::screenTextures[SCREEN_COUNT];83GLuint RenderDevice::imageTexture;8485#if RETRO_PLATFORM == RETRO_ANDROID86#include <ratio>87#include <chrono>88std::chrono::steady_clock::time_point lastFrame;89// Lock at 60 fps on Android because refresh rate is all over the place90using FramePeriod = std::chrono::duration<double, std::ratio<1, 60>>;91FramePeriod targetFreq;92#endif9394int32 RenderDevice::monitorIndex;9596uint32 *RenderDevice::videoBuffer;9798bool32 RenderDevice::isInitialized = false;99100bool RenderDevice::Init()101{102display = eglGetDisplay(EGL_DEFAULT_DISPLAY);103if (!display) {104PrintLog(PRINT_NORMAL, "[EGL] Could not connect to display: %d", eglGetError());105return false;106}107108eglInitialize(display, nullptr, nullptr);109110#if RETRO_PLATFORM == RETRO_SWITCH111if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {112PrintLog(PRINT_NORMAL, "[EGL] eglBindApi failure: %d", eglGetError());113return false;114}115#elif RETRO_PLATFORM == RETRO_ANDROID116#endif117118EGLint numConfigs;119// clang-format off120static const EGLint framebufferAttributeList[] = {121#if RETRO_PLATFORM == RETRO_SWITCH122EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,123#else124EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,125EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,126#endif127EGL_RED_SIZE, 8,128EGL_GREEN_SIZE, 8,129EGL_BLUE_SIZE, 8,130EGL_NONE131};132// clang-format on133eglChooseConfig(display, framebufferAttributeList, &config, 1, &numConfigs);134if (numConfigs == 0) {135PrintLog(PRINT_NORMAL, "[EGL] No configs found: %d", eglGetError());136return false;137}138139if (!SetupRendering())140return false;141if (!isRunning) {142if (!AudioDevice::Init())143return false;144InitInputDevices();145}146isInitialized = true;147return true;148}149150bool RenderDevice::SetupRendering()151{152#if RETRO_PLATFORM == RETRO_SWITCH153window = nwindowGetDefault();154nwindowSetDimensions(window, 1920, 1080);155#elif RETRO_PLATFORM == RETRO_ANDROID156EGLint format;157if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {158PrintLog(PRINT_NORMAL, "[EGL] EGL_NATIVE_VISUAL_ID fetch failed: %d", eglGetError());159return false;160}161if (!window) {162#if RETRO_REV02163if (SKU::userCore)164SKU::userCore->focusState = 1;165#else166engine.focusState = 1;167#endif168return true; // lie so we can properly swtup later169}170ANativeWindow_setBuffersGeometry(window, 0, 0, format);171SwappyGL_setAutoSwapInterval(false);172SwappyGL_setSwapIntervalNS(SWAPPY_SWAP_60FPS);173SwappyGL_setMaxAutoSwapIntervalNS(SWAPPY_SWAP_60FPS);174#endif175176surface = eglCreateWindowSurface(display, config, window, nullptr);177if (!surface) {178PrintLog(PRINT_NORMAL, "[EGL] Surface creation failed: %d", eglGetError());179return false;180}181182#if RETRO_PLATFORM == RETRO_SWITCH183// clang-format off184static const int32 listCount = 1;185static const EGLint attributeListList[listCount][7] = { {186EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,187EGL_CONTEXT_MAJOR_VERSION, 4,188EGL_CONTEXT_MINOR_VERSION, 3,189EGL_NONE190} };191int32 i = 0;192// clang-format on193#elif RETRO_PLATFORM == RETRO_ANDROID194static const int32 listCount = 1;195static const EGLint attributeListList[listCount][3] = { { EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE } }; // lol. lmao196int32 i = 0;197#endif198199context = eglCreateContext(display, config, EGL_NO_CONTEXT, (EGLint *)attributeListList[i]);200while (!context) {201PrintLog(PRINT_NORMAL, "[EGL] Context creation failed: %d", eglGetError());202if (++i < listCount) {203PrintLog(PRINT_NORMAL, "[EGL] Trying next context...");204context = eglCreateContext(display, config, EGL_NO_CONTEXT, (EGLint *)attributeListList[i]);205}206else207return false;208}209210eglMakeCurrent(display, surface, surface, context);211eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &i);212PrintLog(PRINT_NORMAL, "[EGL] Context client version: %d", i);213214GetDisplays();215216if (!InitGraphicsAPI() || !InitShaders())217return false;218219int32 size = videoSettings.pixWidth >= SCREEN_YSIZE ? videoSettings.pixWidth : SCREEN_YSIZE;220if (scanlines)221free(scanlines);222scanlines = (ScanlineInfo *)malloc(size * sizeof(ScanlineInfo));223memset(scanlines, 0, size * sizeof(ScanlineInfo));224225videoSettings.windowState = WINDOWSTATE_ACTIVE;226videoSettings.dimMax = 1.0;227videoSettings.dimPercent = 1.0;228229return true;230}231232void RenderDevice::GetDisplays()233{234235displayCount = 1;236GetWindowSize(&displayWidth[0], &displayHeight[0]);237// reacting to me lying238displayInfo.displays = (decltype(displayInfo.displays))malloc(sizeof(displayInfo.displays->internal));239displayInfo.displays[0].width = displayWidth[0];240displayInfo.displays[0].height = displayHeight[0];241displayInfo.displays[0].refresh_rate = videoSettings.refreshRate;242}243244bool RenderDevice::InitGraphicsAPI()245{246#if RETRO_PLATFORM == RETRO_SWITCH247if (!gladLoadGL()) {248PrintLog(PRINT_NORMAL, "[EGL] gladLoadGL failure");249return false;250}251#else252GLint range[2], precision;253254glGetShaderPrecisionFormat(GL_VERTEX_SHADER, GL_HIGH_FLOAT, range, &precision);255if (!precision)256strcpy(_glVPrecision, "precision mediump float;\n");257else258strcpy(_glVPrecision, "precision highp float;\n");259260glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision);261if (!precision)262strcpy(_glFPrecision, "precision mediump float;\n");263else264strcpy(_glFPrecision, "precision highp float;\n");265266#endif267glClearColor(0.0, 0.0, 0.0, 1.0);268glDisable(GL_DEPTH_TEST);269glDisable(GL_DITHER);270glDisable(GL_BLEND);271glDisable(GL_SCISSOR_TEST);272glDisable(GL_CULL_FACE);273274// setup buffers275#if RETRO_PLATFORM == RETRO_SWITCH276glGenVertexArrays(1, &VAO);277glBindVertexArray(VAO);278#endif279280glGenBuffers(1, &VBO);281glBindBuffer(GL_ARRAY_BUFFER, VBO);282glBufferData(GL_ARRAY_BUFFER, sizeof(RenderVertex) * (!RETRO_REV02 ? 24 : 60), NULL, GL_DYNAMIC_DRAW);283284glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(RenderVertex), 0);285glEnableVertexAttribArray(0);286// glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(RenderVertex), (void *)offsetof(RenderVertex, color));287// glEnableVertexAttribArray(1);288glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RenderVertex), (void *)offsetof(RenderVertex, tex));289glEnableVertexAttribArray(1);290291#if RETRO_PLATFORM == RETRO_SWITCH292videoSettings.fsWidth = 1920;293videoSettings.fsHeight = 1080;294#elif RETRO_PLATFORM == RETRO_ANDROID295customSettings.maxPixWidth = 0;296videoSettings.fsWidth = 0;297videoSettings.fsHeight = 0;298#endif299300// EGL should only be fullscreen only apps301#if false302if (videoSettings.windowed || !videoSettings.exclusiveFS) {303if (videoSettings.windowed) {304viewSize.x = videoSettings.windowWidth;305viewSize.y = videoSettings.windowHeight;306}307else {308viewSize.x = displayWidth[monitorIndex];309viewSize.y = displayHeight[monitorIndex];310}311}312else313#endif314{315int32 bufferWidth = videoSettings.fsWidth;316int32 bufferHeight = videoSettings.fsHeight;317if (videoSettings.fsWidth <= 0 || videoSettings.fsHeight <= 0) {318bufferWidth = displayWidth[monitorIndex];319bufferHeight = displayHeight[monitorIndex];320}321viewSize.x = bufferWidth;322viewSize.y = bufferHeight;323}324325int32 maxPixHeight = 0;326#if !RETRO_USE_ORIGINAL_CODE327int32 screenWidth = 0;328#endif329for (int32 s = 0; s < SCREEN_COUNT; ++s) {330if (videoSettings.pixHeight > maxPixHeight)331maxPixHeight = videoSettings.pixHeight;332333screens[s].size.y = videoSettings.pixHeight;334335float viewAspect = viewSize.x / viewSize.y;336#if !RETRO_USE_ORIGINAL_CODE337screenWidth = (int32)((viewAspect * videoSettings.pixHeight) + 3) & 0xFFFFFFFC;338#else339int32 screenWidth = (int32)((viewAspect * videoSettings.pixHeight) + 3) & 0xFFFFFFFC;340#endif341if (screenWidth < videoSettings.pixWidth)342screenWidth = videoSettings.pixWidth;343344#if !RETRO_USE_ORIGINAL_CODE345if (customSettings.maxPixWidth && screenWidth > customSettings.maxPixWidth)346screenWidth = customSettings.maxPixWidth;347#else348if (screenWidth > DEFAULT_PIXWIDTH)349screenWidth = DEFAULT_PIXWIDTH;350#endif351352memset(&screens[s].frameBuffer, 0, sizeof(screens[s].frameBuffer));353SetScreenSize(s, screenWidth, screens[s].size.y);354}355356pixelSize.x = screens[0].size.x;357pixelSize.y = screens[0].size.y;358float pixAspect = pixelSize.x / pixelSize.y;359360#if RETRO_PLATFORM == RETRO_ANDROID361auto* jni = GetJNISetup();362jni->env->CallVoidMethod(jni->thiz, setPixSize, (int)pixelSize.x, (int)pixelSize.y);363#endif364365Vector2 viewportPos{};366Vector2 viewportSize{ displayWidth[0], displayHeight[0] };367368if ((viewSize.x / viewSize.y) <= (pixAspect + 0.1)) {369if ((pixAspect - 0.1) > (viewSize.x / viewSize.y)) {370viewSize.y = (pixelSize.y / pixelSize.x) * viewSize.x;371viewportPos.y = (displayHeight[0] >> 1) - (viewSize.y * 0.5);372viewportSize.y = viewSize.y;373}374}375else {376viewSize.x = pixAspect * viewSize.y;377viewportPos.x = (displayWidth[0] >> 1) - ((pixAspect * viewSize.y) * 0.5);378viewportSize.x = (pixAspect * viewSize.y);379}380381#if !RETRO_USE_ORIGINAL_CODE382if (screenWidth <= 512 && maxPixHeight <= 256) {383#else384if (maxPixHeight <= 256) {385#endif386textureSize.x = 512.0;387textureSize.y = 256.0;388}389else {390textureSize.x = 1024.0;391textureSize.y = 512.0;392}393394glViewport(viewportPos.x, viewportPos.y, viewportSize.x, viewportSize.y);395396glActiveTexture(GL_TEXTURE0);397glGenTextures(SCREEN_COUNT, screenTextures);398399for (int32 i = 0; i < SCREEN_COUNT; ++i) {400glBindTexture(GL_TEXTURE_2D, screenTextures[i]);401glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureSize.x, textureSize.y, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);402403glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);404glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);405glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);406glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);407}408glGenTextures(1, &imageTexture);409glBindTexture(GL_TEXTURE_2D, imageTexture);410#if RETRO_PLATFORM == RETRO_SWITCH411glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, RETRO_VIDEO_TEXTURE_W, RETRO_VIDEO_TEXTURE_H, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);412#else413glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RETRO_VIDEO_TEXTURE_W, RETRO_VIDEO_TEXTURE_H, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);414#endif415416glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);417glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);418glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);419glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);420421if (videoBuffer)422delete[] videoBuffer;423videoBuffer = new uint32[RETRO_VIDEO_TEXTURE_W * RETRO_VIDEO_TEXTURE_H];424425lastShaderID = -1;426InitVertexBuffer();427engine.inFocus = 1;428videoSettings.viewportX = viewportPos.x;429videoSettings.viewportY = viewportPos.y;430videoSettings.viewportW = 1.0 / viewSize.x;431videoSettings.viewportH = 1.0 / viewSize.y;432433// PrintLog(PRINT_NORMAL, "%d %d %f %f %d %d %d %d %f %f", displayWidth[0], displayHeight[0], pixelSize.x, pixelSize.y, viewportPos.x,434// viewportPos.y,435// viewportSize.x, viewportSize.y, viewSize.x, viewSize.y);436437return true;438}439440// CUSTOM BUFFER FOR SHENANIGAN PURPOSES441// GL hates us and it's coordinate system is reverse of DX442// for shader output equivalency, we havee to flip everything443// X and Y are negated, some verts are specifically moved to match444// U and V are 0/1s and flipped from what it was originally445// clang-format off446#if RETRO_REV02447const RenderVertex rsdkGLVertexBuffer[60] = {448// 1 Screen (0)449{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },450{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },451{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },452{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },453{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },454{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },455456// 2 Screens - Bordered (Top Screen) (6)457{ { +0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },458{ { +0.5, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },459{ { -0.5, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },460{ { +0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },461{ { -0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },462{ { -0.5, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },463464// 2 Screens - Bordered (Bottom Screen) (12)465{ { +0.5, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },466{ { +0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },467{ { -0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },468{ { +0.5, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },469{ { -0.5, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },470{ { -0.5, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },471472// 2 Screens - Stretched (Top Screen) (18)473{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },474{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },475{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },476{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },477{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },478{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },479480// 2 Screens - Stretched (Bottom Screen) (24)481{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },482{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },483{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },484{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },485{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },486{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },487488// 4 Screens (Top-Left) (30)489{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },490{ { 0.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },491{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },492{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },493{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },494{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },495496// 4 Screens (Top-Right) (36)497{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },498{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },499{ { 0.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },500{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },501{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },502{ { 0.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },503504// 4 Screens (Bottom-Right) (42)505{ { 0.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },506{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },507{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },508{ { 0.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },509{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },510{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },511512// 4 Screens (Bottom-Left) (48)513{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },514{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },515{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },516{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },517{ { 0.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },518{ { 0.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },519520// Image/Video (54)521{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },522{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },523{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },524{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },525{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },526{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } }527};528#else529const RenderVertex rsdkGLVertexBuffer[24] =530{531// 1 Screen (0)532{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },533{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },534{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },535{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },536{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },537{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },538539// 2 Screens - Stretched (Top Screen) (6)540{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },541{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },542{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },543{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },544{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },545{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },546547// 2 Screens - Stretched (Bottom Screen) (12)548{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },549{ { +1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },550{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },551{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },552{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },553{ { -1.0, 0.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },554555// Image/Video (18)556{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },557{ { +1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 0.0 } },558{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } },559{ { +1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 1.0, 1.0 } },560{ { -1.0, -1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 1.0 } },561{ { -1.0, +1.0, 1.0 }, 0xFFFFFFFF, { 0.0, 0.0 } }562};563#endif564565566void RenderDevice::InitVertexBuffer()567{568RenderVertex vertBuffer[sizeof(rsdkGLVertexBuffer) / sizeof(RenderVertex)];569memcpy(vertBuffer, rsdkGLVertexBuffer, sizeof(rsdkGLVertexBuffer));570571float x = 0.5 / (float)viewSize.x;572float y = 0.5 / (float)viewSize.y;573574// ignore the last 6 verts, they're scaled to the 1024x512 textures already!575int32 vertCount = (RETRO_REV02 ? 60 : 24) - 6;576for (int32 v = 0; v < vertCount; ++v) {577RenderVertex *vertex = &vertBuffer[v];578vertex->pos.x = vertex->pos.x + x;579vertex->pos.y = vertex->pos.y - y;580581if (vertex->tex.x)582vertex->tex.x = screens[0].size.x * (1.0 / textureSize.x);583584if (vertex->tex.y)585vertex->tex.y = screens[0].size.y * (1.0 / textureSize.y);586}587588glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(RenderVertex) * (!RETRO_REV02 ? 24 : 60), vertBuffer);589}590591void RenderDevice::InitFPSCap() {592#if RETRO_PLATFORM == RETRO_ANDROID593lastFrame = std::chrono::steady_clock::now();594targetFreq = FramePeriod{1}; // One frame (1/60Hz)595#endif596}597598bool RenderDevice::CheckFPSCap() {599#if RETRO_PLATFORM == RETRO_ANDROID600if (lastFrame + targetFreq < std::chrono::steady_clock::now())601return true;602603return false;604#else605return true; // FPS cap using VSync606#endif607}608void RenderDevice::UpdateFPSCap() {609#if RETRO_PLATFORM == RETRO_ANDROID610lastFrame = std::chrono::steady_clock::now();611#endif612}613614void RenderDevice::CopyFrameBuffer()615{616if (!isInitialized)617return;618619for (int32 s = 0; s < videoSettings.screenCount; ++s) {620glBindTexture(GL_TEXTURE_2D, screenTextures[s]);621glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screens[s].pitch, SCREEN_YSIZE, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, screens[s].frameBuffer);622}623}624625bool RenderDevice::ProcessEvents()626{627// events aren't processed by EGL628#if RETRO_PLATFORM == RETRO_ANDROID629// unless you're android!!630int32 events;631struct android_poll_source *source;632while (ALooper_pollAll(0, NULL, &events, (void **)&source) >= 0) {633if (source)634source->process(app, source);635636if (app->destroyRequested) {637isRunning = false;638return false;639}640}641642if (videoSettings.windowState == WINDOWSTATE_INACTIVE) {643Release(true);644}645else if (videoSettings.windowState == WINDOWSTATE_ACTIVE && !RenderDevice::isInitialized && RenderDevice::window) {646Release(true);647Init();648}649#endif650return true;651}652653void RenderDevice::FlipScreen()654{655if (!isInitialized)656return;657658if (lastShaderID != videoSettings.shaderID) {659lastShaderID = videoSettings.shaderID;660661SetLinear(shaderList[videoSettings.shaderID].linear);662663if (videoSettings.shaderSupport)664glUseProgram(shaderList[videoSettings.shaderID].programID);665}666667if (windowRefreshDelay > 0) {668windowRefreshDelay--;669if (!windowRefreshDelay)670UpdateGameWindow();671return;672}673674glClear(GL_COLOR_BUFFER_BIT);675676if (videoSettings.shaderSupport) {677glUniform2fv(glGetUniformLocation(shaderList[videoSettings.shaderID].programID, "textureSize"), 1, &textureSize.x);678glUniform2fv(glGetUniformLocation(shaderList[videoSettings.shaderID].programID, "pixelSize"), 1, &pixelSize.x);679glUniform2fv(glGetUniformLocation(shaderList[videoSettings.shaderID].programID, "viewSize"), 1, &viewSize.x);680glUniform1f(glGetUniformLocation(shaderList[videoSettings.shaderID].programID, "screenDim"), videoSettings.dimMax * videoSettings.dimPercent);681}682683int32 startVert = 0;684switch (videoSettings.screenCount) {685default:686case 0:687#if RETRO_REV02688startVert = 54;689#else690startVert = 18;691#endif692glBindTexture(GL_TEXTURE_2D, imageTexture);693glDrawArrays(GL_TRIANGLES, startVert, 6);694695break;696697case 1:698glBindTexture(GL_TEXTURE_2D, screenTextures[0]);699glDrawArrays(GL_TRIANGLES, 0, 6);700break;701702case 2:703#if RETRO_REV02704startVert = startVertex_2P[0];705#else706startVert = 6;707#endif708glBindTexture(GL_TEXTURE_2D, screenTextures[0]);709glDrawArrays(GL_TRIANGLES, startVert, 6);710711#if RETRO_REV02712startVert = startVertex_2P[1];713#else714startVert = 12;715#endif716glBindTexture(GL_TEXTURE_2D, screenTextures[1]);717glDrawArrays(GL_TRIANGLES, startVert, 6);718break;719720#if RETRO_REV02721case 3:722glBindTexture(GL_TEXTURE_2D, screenTextures[0]);723glDrawArrays(GL_TRIANGLES, startVertex_3P[0], 6);724725glBindTexture(GL_TEXTURE_2D, screenTextures[1]);726glDrawArrays(GL_TRIANGLES, startVertex_3P[1], 6);727728glBindTexture(GL_TEXTURE_2D, screenTextures[2]);729glDrawArrays(GL_TRIANGLES, startVertex_3P[2], 6);730break;731732case 4:733glBindTexture(GL_TEXTURE_2D, screenTextures[0]);734glDrawArrays(GL_TRIANGLES, 30, 6);735736glBindTexture(GL_TEXTURE_2D, screenTextures[1]);737glDrawArrays(GL_TRIANGLES, 36, 6);738739glBindTexture(GL_TEXTURE_2D, screenTextures[2]);740glDrawArrays(GL_TRIANGLES, 42, 6);741742glBindTexture(GL_TEXTURE_2D, screenTextures[3]);743glDrawArrays(GL_TRIANGLES, 48, 6);744break;745#endif746}747748#if RETRO_PLATFORM != RETRO_ANDROID749if (!eglSwapBuffers(display, surface)) {750PrintLog(PRINT_NORMAL, "[EGL] Failed to swap buffers: %d", eglGetError());751}752#else753if (!SwappyGL_swap(display, surface)) {754if (SwappyGL_isEnabled()) {755PrintLog(PRINT_NORMAL, "[EGL] Failed to swap buffers: %d", eglGetError());756}757}758#endif759}760761void RenderDevice::Release(bool32 isRefresh)762{763if (display != EGL_NO_DISPLAY) {764glDeleteTextures(SCREEN_COUNT, screenTextures);765glDeleteTextures(1, &imageTexture);766767if (videoBuffer)768delete[] videoBuffer;769videoBuffer = NULL;770771for (int32 i = 0; i < shaderCount; ++i) {772glDeleteProgram(shaderList[i].programID);773}774775#if RETRO_PLATFORM == RETRO_SWITCH776glDeleteVertexArrays(1, &VAO);777glDeleteBuffers(1, &VBO);778#endif779780eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);781eglDestroyContext(display, context);782eglDestroySurface(display, surface);783eglTerminate(display);784785display = EGL_NO_DISPLAY;786surface = EGL_NO_SURFACE;787context = EGL_NO_CONTEXT;788789isInitialized = false;790}791792if (!isRefresh) {793shaderCount = 0;794#if RETRO_USE_MOD_LOADER795userShaderCount = 0;796#endif797798if (displayInfo.displays)799free(displayInfo.displays);800displayInfo.displays = NULL;801802if (scanlines)803free(scanlines);804scanlines = NULL;805}806}807808bool RenderDevice::InitShaders()809{810videoSettings.shaderSupport = true;811int32 maxShaders = 0;812shaderCount = 0;813814LoadShader("None", false);815LoadShader("Clean", true);816LoadShader("CRT-Yeetron", true);817LoadShader("CRT-Yee64", true);818819#if RETRO_USE_MOD_LOADER820// a place for mods to load custom shaders821RunModCallbacks(MODCB_ONSHADERLOAD, NULL);822userShaderCount = shaderCount;823#endif824825LoadShader("YUV-420", true);826LoadShader("YUV-422", true);827LoadShader("YUV-444", true);828LoadShader("RGB-Image", true);829maxShaders = shaderCount;830831// no shaders == no support832if (!maxShaders) {833ShaderEntry *shader = &shaderList[0];834videoSettings.shaderSupport = false;835836// let's load837maxShaders = 1;838shaderCount = 1;839840GLint success;841char infoLog[0x1000];842843GLuint vert, frag;844const GLchar *vchar[] = { _GLVERSION, _GLDEFINE, _glVPrecision, backupVertex };845vert = glCreateShader(GL_VERTEX_SHADER);846glShaderSource(vert, 4, vchar, NULL);847glCompileShader(vert);848849const GLchar *fchar[] = { _GLVERSION, _GLDEFINE, _glFPrecision, backupFragment };850frag = glCreateShader(GL_FRAGMENT_SHADER);851glShaderSource(frag, 4, fchar, NULL);852glCompileShader(frag);853854glGetShaderiv(vert, GL_COMPILE_STATUS, &success);855if (!success) {856glGetShaderInfoLog(vert, 0x1000, NULL, infoLog);857PrintLog(PRINT_NORMAL, "BACKUP vertex shader compiling failed:\n%s", infoLog);858}859860glGetShaderiv(frag, GL_COMPILE_STATUS, &success);861if (!success) {862glGetShaderInfoLog(frag, 0x1000, NULL, infoLog);863PrintLog(PRINT_NORMAL, "BACKUP fragment shader compiling failed:\n%s", infoLog);864}865866shader->programID = glCreateProgram();867glAttachShader(shader->programID, vert);868glAttachShader(shader->programID, frag);869870glBindAttribLocation(shader->programID, 0, "in_pos");871//glBindAttribLocation(shader->programID, 1, "in_color");872glBindAttribLocation(shader->programID, 1, "in_UV");873874glLinkProgram(shader->programID);875glDeleteShader(vert);876glDeleteShader(frag);877878glUseProgram(shader->programID);879880shader->linear = videoSettings.windowed ? false : shader->linear;881}882883videoSettings.shaderID = MAX(videoSettings.shaderID >= maxShaders ? 0 : videoSettings.shaderID, 0);884SetLinear(shaderList[videoSettings.shaderID].linear || videoSettings.screenCount > 1);885886return true;887}888889void RenderDevice::LoadShader(const char *fileName, bool32 linear)890{891char fullFilePath[0x100];892FileInfo info;893894for (int32 i = 0; i < shaderCount; ++i) {895if (strcmp(shaderList[i].name, fileName) == 0)896return;897}898899if (shaderCount == SHADER_COUNT)900return;901902ShaderEntry *shader = &shaderList[shaderCount];903shader->linear = linear;904sprintf_s(shader->name, sizeof(shader->name), "%s", fileName);905906GLint success;907char infoLog[0x1000];908GLuint vert, frag;909sprintf_s(fullFilePath, sizeof(fullFilePath), "Data/Shaders/OGL/None.vs");910InitFileInfo(&info);911if (LoadFile(&info, fullFilePath, FMODE_RB)) {912uint8 *fileData = NULL;913AllocateStorage((void **)&fileData, info.fileSize + 1, DATASET_TMP, false);914ReadBytes(&info, fileData, info.fileSize);915fileData[info.fileSize] = 0;916CloseFile(&info);917918const GLchar *glchar[] = { _GLVERSION, _GLDEFINE, _glVPrecision, (const GLchar *)fileData };919vert = glCreateShader(GL_VERTEX_SHADER);920glShaderSource(vert, 4, glchar, NULL);921glCompileShader(vert);922RemoveStorageEntry((void **)&fileData);923924glGetShaderiv(vert, GL_COMPILE_STATUS, &success);925if (!success) {926glGetShaderInfoLog(vert, 0x1000, NULL, infoLog);927PrintLog(PRINT_NORMAL, "Vertex shader compiling failed:\n%s", infoLog);928return;929}930}931else932return;933934sprintf_s(fullFilePath, sizeof(fullFilePath), "Data/Shaders/OGL/%s.fs", fileName);935InitFileInfo(&info);936if (LoadFile(&info, fullFilePath, FMODE_RB)) {937uint8 *fileData = NULL;938AllocateStorage((void **)&fileData, info.fileSize + 1, DATASET_TMP, false);939ReadBytes(&info, fileData, info.fileSize);940fileData[info.fileSize] = 0;941CloseFile(&info);942943const GLchar *glchar[] = { _GLVERSION, _GLDEFINE, _glFPrecision, (const GLchar *)fileData };944frag = glCreateShader(GL_FRAGMENT_SHADER);945glShaderSource(frag, 4, glchar, NULL);946glCompileShader(frag);947RemoveStorageEntry((void **)&fileData);948949glGetShaderiv(frag, GL_COMPILE_STATUS, &success);950if (!success) {951glGetShaderInfoLog(frag, 0x1000, NULL, infoLog);952PrintLog(PRINT_NORMAL, "Fragment shader compiling failed:\n%s", infoLog);953return;954}955}956else957return;958959shader->programID = glCreateProgram();960glAttachShader(shader->programID, vert);961glAttachShader(shader->programID, frag);962963glBindAttribLocation(shader->programID, 0, "in_pos");964//glBindAttribLocation(shader->programID, 1, "in_color");965glBindAttribLocation(shader->programID, 1, "in_UV");966967glLinkProgram(shader->programID);968glGetProgramiv(shader->programID, GL_LINK_STATUS, &success);969if (!success) {970glGetProgramInfoLog(shader->programID, 0x1000, NULL, infoLog);971PrintLog(PRINT_NORMAL, "OpenGL shader linking failed:\n%s", infoLog);972return;973}974glDeleteShader(vert);975glDeleteShader(frag);976shaderCount++;977};978979void RenderDevice::RefreshWindow()980{981// do nothing probably982// there's literally 0 moment where this is needed983}984985void RenderDevice::GetWindowSize(int32 *width, int32 *height)986{987#if RETRO_PLATFORM == RETRO_ANDROID988if (width)989eglQuerySurface(display, surface, EGL_WIDTH, width);990if (height)991eglQuerySurface(display, surface, EGL_HEIGHT, height);992#elif RETRO_PLATFORM == RETRO_SWITCH993if (width)994*width = 1920;995if (height)996*height = 1080;997#endif998}9991000void RenderDevice::SetupImageTexture(int32 width, int32 height, uint8 *imagePixels)1001{1002if (imagePixels && isInitialized) {1003glBindTexture(GL_TEXTURE_2D, imageTexture);1004glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, imagePixels);1005}1006}10071008void RenderDevice::SetupVideoTexture_YUV420(int32 width, int32 height, uint8 *yPlane, uint8 *uPlane, uint8 *vPlane, int32 strideY, int32 strideU,1009int32 strideV)1010{1011if (!isInitialized)1012return;1013uint32 *pixels = videoBuffer;1014uint32 *preY = pixels;1015int32 pitch = RETRO_VIDEO_TEXTURE_W - width;1016if (videoSettings.shaderSupport) {1017for (int32 y = 0; y < height; ++y) {1018for (int32 x = 0; x < width; ++x) {1019*pixels++ = (yPlane[x] << _YOFF) | 0xFF000000;1020}10211022pixels += pitch;1023yPlane += strideY;1024}10251026pixels = preY;1027pitch = RETRO_VIDEO_TEXTURE_W - (width >> 1);1028for (int32 y = 0; y < (height >> 1); ++y) {1029for (int32 x = 0; x < (width >> 1); ++x) {1030*pixels++ |= (vPlane[x] << _VOFF) | (uPlane[x] << _UOFF) | 0xFF000000;1031}10321033pixels += pitch;1034uPlane += strideU;1035vPlane += strideV;1036}1037}1038else {1039// No shader support means no YUV support! at least use the brightness to show it in grayscale!1040for (int32 y = 0; y < height; ++y) {1041for (int32 x = 0; x < width; ++x) {1042int32 brightness = yPlane[x];1043*pixels++ = (brightness << 0) | (brightness << 8) | (brightness << 16) | 0xFF000000;1044}10451046pixels += pitch;1047yPlane += strideY;1048}1049}10501051glBindTexture(GL_TEXTURE_2D, imageTexture);1052glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, RETRO_VIDEO_TEXTURE_W, RETRO_VIDEO_TEXTURE_H, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, videoBuffer);1053}10541055void RenderDevice::SetupVideoTexture_YUV422(int32 width, int32 height, uint8 *yPlane, uint8 *uPlane, uint8 *vPlane, int32 strideY, int32 strideU,1056int32 strideV)1057{1058if (!isInitialized)1059return;1060uint32 *pixels = videoBuffer;1061uint32 *preY = pixels;1062int32 pitch = RETRO_VIDEO_TEXTURE_W - width;10631064if (videoSettings.shaderSupport) {1065for (int32 y = 0; y < height; ++y) {1066for (int32 x = 0; x < width; ++x) {1067*pixels++ = (yPlane[x] << _YOFF) | 0xFF000000;1068}10691070pixels += pitch;1071yPlane += strideY;1072}10731074pixels = preY;1075pitch = RETRO_VIDEO_TEXTURE_W - (width >> 1);1076for (int32 y = 0; y < height; ++y) {1077for (int32 x = 0; x < (width >> 1); ++x) {1078*pixels++ |= (vPlane[x] << _VOFF) | (uPlane[x] << _UOFF) | 0xFF000000;1079}10801081pixels += pitch;1082uPlane += strideU;1083vPlane += strideV;1084}1085}1086else {1087// No shader support means no YUV support! at least use the brightness to show it in grayscale!1088for (int32 y = 0; y < height; ++y) {1089for (int32 x = 0; x < width; ++x) {1090int32 brightness = yPlane[x];1091*pixels++ = (brightness << 0) | (brightness << 8) | (brightness << 16) | 0xFF000000;1092}10931094pixels += pitch;1095yPlane += strideY;1096}1097}10981099glBindTexture(GL_TEXTURE_2D, imageTexture);1100glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, RETRO_VIDEO_TEXTURE_W, RETRO_VIDEO_TEXTURE_H, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, videoBuffer);1101}1102void RenderDevice::SetupVideoTexture_YUV444(int32 width, int32 height, uint8 *yPlane, uint8 *uPlane, uint8 *vPlane, int32 strideY, int32 strideU,1103int32 strideV)1104{1105if (!isInitialized)1106return;1107uint32 *pixels = videoBuffer;1108int32 pitch = RETRO_VIDEO_TEXTURE_W - width;1109if (videoSettings.shaderSupport) {1110for (int32 y = 0; y < height; ++y) {1111int32 pos1 = yPlane - vPlane;1112int32 pos2 = uPlane - vPlane;1113uint8 *pixV = vPlane;1114for (int32 x = 0; x < width; ++x) {1115*pixels++ = (pixV[0] << _VOFF) | (pixV[pos2] << _UOFF) | (pixV[pos1] << _YOFF) | 0xFF000000;1116pixV++;1117}11181119pixels += pitch;1120yPlane += strideY;1121uPlane += strideU;1122vPlane += strideV;1123}1124}1125else {1126// No shader support means no YUV support! at least use the brightness to show it in grayscale!1127for (int32 y = 0; y < height; ++y) {1128for (int32 x = 0; x < width; ++x) {1129int32 brightness = yPlane[x];1130*pixels++ = (brightness << 0) | (brightness << 8) | (brightness << 16) | 0xFF000000;1131}11321133pixels += pitch;1134yPlane += strideY;1135}1136}11371138glBindTexture(GL_TEXTURE_2D, imageTexture);1139glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, RETRO_VIDEO_TEXTURE_W, RETRO_VIDEO_TEXTURE_H, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, videoBuffer);1140}11411142void RenderDevice::SetLinear(bool32 linear)1143{1144if (!isInitialized)1145return;1146for (int32 i = 0; i < SCREEN_COUNT; ++i) {1147glBindTexture(GL_TEXTURE_2D, screenTextures[i]);1148glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);1149glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);1150}1151}115211531154