diff --git a/.github/gles.patch b/.github/gles.patch index 548b243d..b16733b5 100644 --- a/.github/gles.patch +++ b/.github/gles.patch @@ -1,25 +1,3 @@ -diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag -index 612671c8..1937f711 100644 ---- a/src/host_shaders/opengl_display.frag -+++ b/src/host_shaders/opengl_display.frag -@@ -1,4 +1,5 @@ --#version 410 core -+#version 300 es -+precision mediump float; - in vec2 UV; - out vec4 FragColor; - -diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert -index 990e2f80..2e7842ac 100644 ---- a/src/host_shaders/opengl_display.vert -+++ b/src/host_shaders/opengl_display.vert -@@ -1,4 +1,5 @@ --#version 410 core -+#version 300 es -+precision mediump float; - out vec2 UV; - - void main() { diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag index 9f07df0b..96a35afa 100644 --- a/src/host_shaders/opengl_fragment_shader.frag diff --git a/CMakeLists.txt b/CMakeLists.txt index c38a6af2..be70036c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF) option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON) option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON) option(ENABLE_METAL "Enable Metal rendering backend (if available)" ON) +option(ENABLE_WAYLAND "Enable Wayland support on Linux platforms" ON) option(ENABLE_LTO "Enable link-time optimization" OFF) option(ENABLE_TESTS "Compile unit-tests" OFF) option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF) @@ -88,7 +89,7 @@ endif() # Generate versioning files find_package(Git) -set(PANDA3DS_VERSION "0.8") +set(PANDA3DS_VERSION "0.9") if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in) file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"") @@ -97,22 +98,29 @@ endif() if(GIT_FOUND AND ENABLE_GIT_VERSIONING) execute_process( WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0 - OUTPUT_VARIABLE PANDA3DS_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) - execute_process( - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(NOT PANDA3DS_VERSION STREQUAL git_version_tag) - execute_process( - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=7 - OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE - ) - set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}") - unset(git_version_rev) + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD + OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT git_version_tag STREQUAL "") + set(PANDA3DS_VERSION "${git_version_tag}") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags + OUTPUT_VARIABLE git_version_desc OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(git_version_tag STREQUAL git_version_desc) + set(git_version_rev "") + endif() + unset(git_version_desc) + endif() + if(NOT git_version_rev STREQUAL "") + set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}") endif() string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}") unset(git_version_tag) + unset(git_version_rev) endif() configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp) include_directories(${CMAKE_BINARY_DIR}/include/) @@ -140,6 +148,10 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything add_compile_definitions(SDL_MAIN_HANDLED) +if(ENABLE_WAYLAND) + add_compile_definitions(WAYLAND_ENABLED) +endif() + if(ENABLE_DISCORD_RPC AND NOT ANDROID) add_subdirectory(third_party/discord-rpc) include_directories(third_party/discord-rpc/include) @@ -422,16 +434,22 @@ if(ENABLE_LUAJIT AND NOT ANDROID) target_link_libraries(AlberCore PRIVATE uv_a) endif() +set(GL_CONTEXT_SOURCE_FILES "") + if(ENABLE_QT_GUI) - set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp) + set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp) if(APPLE) - set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm) + set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm) elseif(WIN32) - set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp) + set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp) else() - set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_wayland.cpp - third_party/duckstation/gl/context_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp) + set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_x11.cpp + third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp) + + if(ENABLE_WAYLAND) + set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl_wayland.cpp) + endif() endif() endif() @@ -445,7 +463,7 @@ source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES}) source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES}) source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES}) source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES}) -source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) +source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES} ${GL_CONTEXT_SOURCE_FILES}) set(RENDERER_GL_SOURCE_FILES "") # Empty by default unless we are compiling with the GL renderer set(RENDERER_VK_SOURCE_FILES "") # Empty by default unless we are compiling with the VK renderer @@ -460,8 +478,9 @@ if(ENABLE_OPENGL) set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp - src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag - src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert + src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert + src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert + src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert src/host_shaders/opengl_fragment_shader.frag ) @@ -474,8 +493,10 @@ if(ENABLE_OPENGL) resources_renderer_gl NAMESPACE RendererGL WHENCE "src/host_shaders/" - "src/host_shaders/opengl_display.frag" "src/host_shaders/opengl_display.vert" + "src/host_shaders/opengl_display.frag" + "src/host_shaders/opengl_es_display.vert" + "src/host_shaders/opengl_es_display.frag" "src/host_shaders/opengl_vertex_shader.vert" "src/host_shaders/opengl_fragment_shader.frag" ) @@ -751,7 +772,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE) endif() target_link_libraries(Alber PRIVATE AlberCore) - target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${APP_RESOURCES}) + target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES}) elseif(BUILD_HYDRA_CORE) target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1) include_directories(third_party/hydra_core/include) diff --git a/include/config.hpp b/include/config.hpp index 5301ae94..d45aa05c 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -49,6 +49,12 @@ struct EmulatorConfig { #endif static constexpr bool accelerateShadersDefault = true; +#if defined(__LIBRETRO__) + static constexpr bool audioEnabledDefault = true; +#else + static constexpr bool audioEnabledDefault = false; +#endif + bool shaderJitEnabled = shaderJitDefault; bool useUbershaders = ubershaderDefault; bool accelerateShaders = accelerateShadersDefault; @@ -66,7 +72,7 @@ struct EmulatorConfig { bool sdWriteProtected = false; bool usePortableBuild = false; - bool audioEnabled = false; + bool audioEnabled = audioEnabledDefault; bool vsyncEnabled = true; bool aacEnabled = true; // Enable AAC audio? diff --git a/include/emulator.hpp b/include/emulator.hpp index cf231328..a222a021 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -89,7 +89,6 @@ class Emulator { ~Emulator(); void step(); - void render(); void reset(ReloadOption reload); void runFrame(); // Poll the scheduler for events diff --git a/include/renderer.hpp b/include/renderer.hpp index bc5dfac6..b458ecce 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -81,6 +81,10 @@ class Renderer { virtual std::string getUbershader() { return ""; } virtual void setUbershader(const std::string& shader) {} + // Only relevant for OpenGL renderer and other OpenGL-based backends (eg software) + // Called to notify the core to use OpenGL ES and not desktop GL + virtual void setupGLES() {} + // This function is called on every draw call before parsing vertex data. // It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between // ubershaders and shadergen, and so on. diff --git a/include/renderer_gl/gl_driver.hpp b/include/renderer_gl/gl_driver.hpp index a15c061f..4a0b3727 100644 --- a/include/renderer_gl/gl_driver.hpp +++ b/include/renderer_gl/gl_driver.hpp @@ -4,6 +4,7 @@ // Stuff like whether specific extensions are supported, and potentially things like OpenGL context information namespace OpenGL { struct Driver { + bool usingGLES = false; bool supportsExtFbFetch = false; bool supportsArmFbFetch = false; diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index fab239f2..a862cd26 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -40,7 +40,7 @@ class RendererGL final : public Renderer { OpenGL::VertexArray hwShaderVAO; OpenGL::VertexBuffer vbo; - // Data + // Data struct { // TEV configuration uniform locations GLint textureEnvSourceLoc = -1; @@ -157,6 +157,7 @@ class RendererGL final : public Renderer { void initGraphicsContextInternal(); void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel); + void compileDisplayShader(); public: RendererGL(GPU& gpu, const std::array& internalRegs, const std::array& externalRegs) @@ -169,14 +170,15 @@ class RendererGL final : public Renderer { void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override; - void drawVertices(PICA::PrimType primType, std::span vertices) override; // Draw the given vertices + void drawVertices(PICA::PrimType primType, std::span vertices) override; // Draw the given vertices void deinitGraphicsContext() override; virtual bool supportsShaderReload() override { return true; } virtual std::string getUbershader() override; virtual void setUbershader(const std::string& shader) override; virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override; - + virtual void setupGLES() override; + std::optional getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true); // Note: The caller is responsible for deleting the currently bound FBO before calling this diff --git a/src/config.cpp b/src/config.cpp index 5d9791c6..9b262744 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -27,6 +27,7 @@ void EmulatorConfig::load() { return; } + printf("Loading existing configuration file %s\n", path.string().c_str()); toml::value data; try { @@ -101,7 +102,7 @@ void EmulatorConfig::load() { auto dspCoreName = toml::find_or(audio, "DSPEmulation", "HLE"); dspType = Audio::DSPCore::typeFromString(dspCoreName); - audioEnabled = toml::find_or(audio, "EnableAudio", false); + audioEnabled = toml::find_or(audio, "EnableAudio", audioEnabledDefault); aacEnabled = toml::find_or(audio, "EnableAACAudio", true); printDSPFirmware = toml::find_or(audio, "PrintDSPFirmware", false); diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 90b8f910..c1899655 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -51,17 +51,12 @@ void RendererGL::reset() { gl.useProgram(oldProgram); // Switch to old GL program } - -#ifdef USING_GLES - fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL); -#endif } void RendererGL::initGraphicsContextInternal() { gl.reset(); auto gl_resources = cmrc::RendererGL::get_filesystem(); - auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert"); auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag"); @@ -70,16 +65,7 @@ void RendererGL::initGraphicsContextInternal() { triangleProgram.create({vert, frag}); initUbershader(triangleProgram); - auto displayVertexShaderSource = gl_resources.open("opengl_display.vert"); - auto displayFragmentShaderSource = gl_resources.open("opengl_display.frag"); - - OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex); - OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment); - displayProgram.create({vertDisplay, fragDisplay}); - - gl.useProgram(displayProgram); - glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object - + compileDisplayShader(); // Create stream buffers for vertex, index and uniform buffers static constexpr usize hwIndexBufferSize = 2_MB; static constexpr usize hwVertexBufferSize = 16_MB; @@ -191,6 +177,7 @@ void RendererGL::initGraphicsContextInternal() { } reset(); + fragShaderGen.setTarget(driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL); // Populate our driver info structure driverInfo.supportsExtFbFetch = (GLAD_GL_EXT_shader_framebuffer_fetch != 0); @@ -850,9 +837,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() { PICA::FragmentConfig fsConfig(regs); // If we're not on GLES, ignore the logic op configuration and don't generate redundant shaders for it, since we use hw logic ops -#ifndef USING_GLES - fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0); -#endif + if (!driverInfo.usingGLES) { + fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0); + } OpenGL::Shader& fragShader = shaderCache.fragmentShaderCache[fsConfig]; if (!fragShader.exists()) { @@ -1010,7 +997,7 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* std::string picaShaderSource = PICA::ShaderGen::decompileShader( shaderUnit.vs, *emulatorConfig, shaderUnit.vs.entrypoint, - Helpers::isAndroid() ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL + driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL ); // Empty source means compilation error, if the source is not empty then we convert the recompiled PICA code into a valid shader and upload @@ -1156,6 +1143,19 @@ void RendererGL::initUbershader(OpenGL::Program& program) { glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3); } +void RendererGL::compileDisplayShader() { + auto gl_resources = cmrc::RendererGL::get_filesystem(); + auto displayVertexShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.vert") : gl_resources.open("opengl_display.vert"); + auto displayFragmentShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.frag") : gl_resources.open("opengl_display.frag"); + + OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex); + OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment); + displayProgram.create({vertDisplay, fragDisplay}); + + gl.useProgram(displayProgram); + glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object +} + void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) { u32 buffer = 0; // Vertex buffer index for non-fixed attributes u32 attrCount = 0; @@ -1250,4 +1250,20 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele ); } } -} \ No newline at end of file +} + +void RendererGL::setupGLES() { + driverInfo.usingGLES = true; + + // OpenGL ES hardware is typically way too slow to use the ubershader (eg RPi, mobile phones, handhelds) or has other issues with it. + // So, display a warning and turn them off on OpenGL ES. + if (emulatorConfig->useUbershaders) { + emulatorConfig->useUbershaders = false; + Helpers::warn("Ubershaders enabled on OpenGL ES. This usually results in a worse experience, turning it off..."); + } + + // Stub out logic operations so that calling them doesn't crash the emulator + if (!glLogicOp) { + glLogicOp = [](GLenum) {}; + } +} diff --git a/src/emulator.cpp b/src/emulator.cpp index 86adbf22..11970d91 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -105,13 +105,18 @@ std::filesystem::path Emulator::getConfigPath() { if constexpr (Helpers::isAndroid()) { return getAndroidAppPath() / "config.toml"; } else { - return std::filesystem::current_path() / "config.toml"; + std::filesystem::path localPath = std::filesystem::current_path() / "config.toml"; + + if (std::filesystem::exists(localPath)) { + return localPath; + } else { + return getAppDataRoot() / "config.toml"; + } } } #endif void Emulator::step() {} -void Emulator::render() {} // Only resume if a ROM is properly loaded void Emulator::resume() { @@ -468,4 +473,4 @@ void Emulator::reloadSettings() { } } #endif -} \ No newline at end of file +} diff --git a/src/host_shaders/opengl_es_display.frag b/src/host_shaders/opengl_es_display.frag new file mode 100644 index 00000000..600ebfcd --- /dev/null +++ b/src/host_shaders/opengl_es_display.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; + +in vec2 UV; +out vec4 FragColor; + +uniform sampler2D u_texture; +void main() { + FragColor = texture(u_texture, UV); +} \ No newline at end of file diff --git a/src/host_shaders/opengl_es_display.vert b/src/host_shaders/opengl_es_display.vert new file mode 100644 index 00000000..04fadfc6 --- /dev/null +++ b/src/host_shaders/opengl_es_display.vert @@ -0,0 +1,25 @@ +#version 310 es +precision mediump float; + +out vec2 UV; + +void main() { + const vec4 positions[4] = vec4[]( + vec4(-1.0, 1.0, 1.0, 1.0), // Top-left + vec4(1.0, 1.0, 1.0, 1.0), // Top-right + vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left + vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right + ); + + // The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise + // So we adjust our texcoords accordingly + const vec2 texcoords[4] = vec2[]( + vec2(1.0, 1.0), // Top-right + vec2(1.0, 0.0), // Bottom-right + vec2(0.0, 1.0), // Top-left + vec2(0.0, 0.0) // Bottom-left + ); + + gl_Position = positions[gl_VertexID]; + UV = texcoords[gl_VertexID]; +} \ No newline at end of file diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index 078b8a6c..0bcd21a8 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -118,6 +118,7 @@ void HydraCore::resetContext() { if (!gladLoadGLES2Loader(reinterpret_cast(getProcAddress))) { Helpers::panic("OpenGL ES init failed"); } + emulator->getRenderer()->setupGLES(); #else if (!gladLoadGLLoader(reinterpret_cast(getProcAddress))) { Helpers::panic("OpenGL init failed"); diff --git a/src/jni_driver.cpp b/src/jni_driver.cpp index 60bbc680..6a156360 100644 --- a/src/jni_driver.cpp +++ b/src/jni_driver.cpp @@ -78,6 +78,7 @@ AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) { } __android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES %d.%d", GLVersion.major, GLVersion.minor); + emulator->getRenderer()->setupGLES(); emulator->initGraphicsContext(nullptr); } @@ -153,7 +154,6 @@ int AndroidUtils::openDocument(const char* path, const char* perms) { jstring uri = env->NewStringUTF(path); jstring jmode = env->NewStringUTF(perms); - jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode); env->DeleteLocalRef(uri); diff --git a/src/libretro_core.cpp b/src/libretro_core.cpp index 1e5b532d..727da8d2 100644 --- a/src/libretro_core.cpp +++ b/src/libretro_core.cpp @@ -17,7 +17,8 @@ static retro_input_state_t inputStateCallback; static retro_hw_render_callback hwRender; static std::filesystem::path savePath; -static bool screenTouched; +static bool screenTouched = false; +static bool usingGLES = false; std::unique_ptr emulator; RendererGL* renderer; @@ -35,15 +36,19 @@ static void* getGLProcAddress(const char* name) { } static void videoResetContext() { -#ifdef USING_GLES - if (!gladLoadGLES2Loader(reinterpret_cast(getGLProcAddress))) { - Helpers::panic("OpenGL ES init failed"); + if (usingGLES) { + if (!gladLoadGLES2Loader(reinterpret_cast(getGLProcAddress))) { + Helpers::panic("OpenGL ES init failed"); + } + + emulator->getRenderer()->setupGLES(); } -#else - if (!gladLoadGLLoader(reinterpret_cast(getGLProcAddress))) { - Helpers::panic("OpenGL init failed"); + + else { + if (!gladLoadGLLoader(reinterpret_cast(getGLProcAddress))) { + Helpers::panic("OpenGL init failed"); + } } -#endif emulator->initGraphicsContext(nullptr); } @@ -73,6 +78,7 @@ static bool setHWRender(retro_hw_context_type type) { hwRender.version_minor = 1; if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) { + usingGLES = true; return true; } break; @@ -170,8 +176,9 @@ static void configInit() { {"panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault ? "Use ubershaders (No stutter, maybe slower); enabled|disabled" : "Use ubershaders (No stutter, maybe slower); disabled|enabled"}, {"panda3ds_use_vsync", "Enable VSync; enabled|disabled"}, + {"panda3ds_system_language", "System language; En|Fr|Es|De|It|Pt|Nl|Ru|Ja|Zh|Ko|Tw"}, {"panda3ds_dsp_emulation", "DSP emulation; HLE|LLE|Null"}, - {"panda3ds_use_audio", "Enable audio; disabled|enabled"}, + {"panda3ds_use_audio", EmulatorConfig::audioEnabledDefault ? "Enable audio; enabled|disabled" : "Enable audio; disabled|enabled"}, {"panda3ds_audio_volume", "Audio volume; 100|0|10|20|40|60|80|90|100|120|140|150|180|200"}, {"panda3ds_mute_audio", "Mute audio; disabled|enabled"}, {"panda3ds_enable_aac", "Enable AAC audio; enabled|disabled"}, @@ -196,6 +203,8 @@ static void configUpdate() { config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault); config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true); config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100); + config.systemLanguage = EmulatorConfig::languageCodeFromString(fetchVariable("panda3ds_system_language", "en")); + config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null")); config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false); config.aacEnabled = fetchVariableBool("panda3ds_enable_aac", true); diff --git a/src/panda_qt/config_window.cpp b/src/panda_qt/config_window.cpp index 5065ffa1..a4e43f52 100644 --- a/src/panda_qt/config_window.cpp +++ b/src/panda_qt/config_window.cpp @@ -190,8 +190,18 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win rendererType->addItem(tr("Vulkan")); rendererType->setCurrentIndex(static_cast(config.rendererType)); connect(rendererType, &QComboBox::currentIndexChanged, this, [&](int index) { - config.rendererType = static_cast(index); - updateConfig(); + auto type = static_cast(index); + + if (type == RendererType::Vulkan) { + QMessageBox messageBox( + QMessageBox::Icon::Critical, tr("Vulkan renderer unavailable"), + tr("Qt UI doesn't currently support Vulkan, try again at a later time") + ); + messageBox.exec(); + } else { + config.rendererType = type; + updateConfig(); + } }); gpuLayout->addRow(tr("GPU renderer"), rendererType); diff --git a/src/panda_qt/main.cpp b/src/panda_qt/main.cpp index a7a6216c..4ab737b0 100644 --- a/src/panda_qt/main.cpp +++ b/src/panda_qt/main.cpp @@ -7,6 +7,5 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window(&app); - window.show(); return app.exec(); } diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 881dc02d..c060318e 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -22,14 +22,13 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) // Enable drop events for loading ROMs setAcceptDrops(true); resize(800, 240 * 4); + show(); // We pass a callback to the screen widget that will be triggered every time we resize the screen screen = new ScreenWidget([this](u32 width, u32 height) { handleScreenResize(width, height); }, this); setCentralWidget(screen); - screen->show(); appRunning = true; - // Set our menu bar up menuBar = new QMenuBar(nullptr); @@ -140,6 +139,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) glContext->MakeCurrent(); glContext->SetSwapInterval(emu->getConfig().vsyncEnabled ? 1 : 0); + if (glContext->IsGLES()) { + emu->getRenderer()->setupGLES(); + } + emu->initGraphicsContext(glContext); } else if (usingVk) { Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!"); @@ -695,4 +698,4 @@ void MainWindow::setupControllerSensors(SDL_GameController* controller) { if (haveAccelerometer) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); } -} \ No newline at end of file +} diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp index 25ff576c..fc783683 100644 --- a/src/panda_qt/screen.cpp +++ b/src/panda_qt/screen.cpp @@ -29,6 +29,7 @@ ScreenWidget::ScreenWidget(ResizeCallback resizeCallback, QWidget* parent) : QWi setAttribute(Qt::WA_KeyCompression, false); setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); + show(); if (!createGLContext()) { Helpers::panic("Failed to create GL context for display"); @@ -60,11 +61,12 @@ void ScreenWidget::resizeSurface(u32 width, u32 height) { } bool ScreenWidget::createGLContext() { - // List of GL context versions we will try. Anything 4.1+ is good - static constexpr std::array versionsToTry = { + // List of GL context versions we will try. Anything 4.1+ is good for desktop OpenGL, and 3.1+ for OpenGL ES + static constexpr std::array versionsToTry = { GL::Context::Version{GL::Context::Profile::Core, 4, 6}, GL::Context::Version{GL::Context::Profile::Core, 4, 5}, GL::Context::Version{GL::Context::Profile::Core, 4, 4}, GL::Context::Version{GL::Context::Profile::Core, 4, 3}, GL::Context::Version{GL::Context::Profile::Core, 4, 2}, GL::Context::Version{GL::Context::Profile::Core, 4, 1}, + GL::Context::Version{GL::Context::Profile::ES, 3, 2}, GL::Context::Version{GL::Context::Profile::ES, 3, 1}, }; std::optional windowInfo = getWindowInfo(); @@ -72,6 +74,10 @@ bool ScreenWidget::createGLContext() { this->windowInfo = *windowInfo; glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + if (glContext == nullptr) { + return false; + } + glContext->DoneCurrent(); } @@ -79,7 +85,7 @@ bool ScreenWidget::createGLContext() { } qreal ScreenWidget::devicePixelRatioFromScreen() const { - const QScreen* screenForRatio = window()->windowHandle()->screen(); + const QScreen* screenForRatio = windowHandle()->screen(); if (!screenForRatio) { screenForRatio = QGuiApplication::primaryScreen(); } diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index 1c07c25e..2d60d2fa 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -71,11 +71,27 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp glContext = SDL_GL_CreateContext(window); if (glContext == nullptr) { - Helpers::panic("OpenGL context creation failed: %s", SDL_GetError()); - } + Helpers::warn("OpenGL context creation failed: %s\nTrying again with OpenGL ES.", SDL_GetError()); - if (!gladLoadGLLoader(reinterpret_cast(SDL_GL_GetProcAddress))) { - Helpers::panic("OpenGL init failed"); + // Some low end devices (eg RPi, emulation handhelds) don't support desktop GL, but only OpenGL ES, so fall back to that if GL context + // creation failed + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + glContext = SDL_GL_CreateContext(window); + if (glContext == nullptr) { + Helpers::panic("OpenGL context creation failed: %s", SDL_GetError()); + } + + if (!gladLoadGLES2Loader(reinterpret_cast(SDL_GL_GetProcAddress))) { + Helpers::panic("OpenGL init failed"); + } + + emu.getRenderer()->setupGLES(); + } else { + if (!gladLoadGLLoader(reinterpret_cast(SDL_GL_GetProcAddress))) { + Helpers::panic("OpenGL init failed"); + } } SDL_GL_SetSwapInterval(config.vsyncEnabled ? 1 : 0);