From 1b0c08cad23285d299937661cd5bf5471db43314 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 12 Dec 2024 21:21:49 +0100 Subject: [PATCH 01/13] Prevent selecting Vulkan renderer in Qt frontend and present a message --- src/panda_qt/config_window.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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); From 7c5f7954e591612a5bd6683329a479a9df6ca9de Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:41:55 +0200 Subject: [PATCH 02/13] Libretro: Add system language option --- include/config.hpp | 3 ++- src/config.cpp | 2 +- src/libretro_core.cpp | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 5301ae94..749ba3af 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -48,6 +48,7 @@ struct EmulatorConfig { static constexpr bool ubershaderDefault = true; #endif static constexpr bool accelerateShadersDefault = true; + static constexpr bool audioEnabledDefault = true; bool shaderJitEnabled = shaderJitDefault; bool useUbershaders = ubershaderDefault; @@ -66,7 +67,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/src/config.cpp b/src/config.cpp index 5d9791c6..bfe84193 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -101,7 +101,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/libretro_core.cpp b/src/libretro_core.cpp index 1e5b532d..fe3cb6c4 100644 --- a/src/libretro_core.cpp +++ b/src/libretro_core.cpp @@ -170,8 +170,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 +197,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); From 5a2a33224ca54ca0334d5184d4e1e928fea954eb Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:42:33 +0200 Subject: [PATCH 03/13] Only enable audio by default on libretro for now --- include/config.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/config.hpp b/include/config.hpp index 749ba3af..d45aa05c 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -48,7 +48,12 @@ struct EmulatorConfig { static constexpr bool ubershaderDefault = true; #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; From 8cc9bfbb36b2656f05c38bbb01275ef8c8f43c3e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:59:26 +0200 Subject: [PATCH 04/13] CMake: Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c38a6af2..49546b7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,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}\"") From e8c0b7f9c550965fd17ea2689a570eaf8f887e78 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 26 Dec 2024 22:26:37 +0100 Subject: [PATCH 05/13] Store configuration file in AppData root if not in working directory (#693) * Store configuration file in AppData root if not in working directory This fixes MacOS app bundles, as the emulator cannot write the config file into the app bundle. * Remove duplicate fs calls * I'm an idiot sandwich --------- Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> --- src/config.cpp | 1 + src/emulator.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index bfe84193..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 { diff --git a/src/emulator.cpp b/src/emulator.cpp index 86adbf22..81a18f30 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -105,7 +105,13 @@ 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 @@ -468,4 +474,4 @@ void Emulator::reloadSettings() { } } #endif -} \ No newline at end of file +} From 7c2918f3f749b9f93e7bd69fdff7055d29164457 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:45:28 +0200 Subject: [PATCH 06/13] GL: Add usingGLES to driverInfo struct (#694) --- include/renderer_gl/gl_driver.hpp | 1 + src/core/renderer_gl/renderer_gl.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) 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/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 90b8f910..35de68ce 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -51,17 +51,16 @@ 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(); +#if defined(USING_GLES) || defined(__ANDROID__) + driverInfo.usingGLES = true; +#endif + 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"); @@ -191,6 +190,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 +850,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 +1010,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 From 80ccede765b83595ebbba9879c7dc4a6bfaa7127 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:14:11 +0200 Subject: [PATCH 07/13] Wayland fixes part 1 --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49546b7d..7bd8b9ed 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) @@ -140,6 +141,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) From cb8b13e12927cdfea7f0c8bf8f47047d26e3cd3c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 28 Dec 2024 19:52:03 +0200 Subject: [PATCH 08/13] Support GLES on desktop --- .github/gles.patch | 22 ------------- CMakeLists.txt | 9 ++++-- include/emulator.hpp | 1 - include/renderer.hpp | 4 +++ include/renderer_gl/renderer_gl.hpp | 8 +++-- src/core/renderer_gl/renderer_gl.cpp | 42 ++++++++++++++++--------- src/emulator.cpp | 1 - src/host_shaders/opengl_es_display.frag | 10 ++++++ src/host_shaders/opengl_es_display.vert | 25 +++++++++++++++ src/hydra_core.cpp | 1 + src/jni_driver.cpp | 2 +- src/libretro_core.cpp | 22 ++++++++----- src/panda_qt/main_window.cpp | 4 +++ src/panda_qt/screen.cpp | 9 ++++-- src/panda_sdl/frontend_sdl.cpp | 24 +++++++++++--- 15 files changed, 125 insertions(+), 59 deletions(-) create mode 100644 src/host_shaders/opengl_es_display.frag create mode 100644 src/host_shaders/opengl_es_display.vert 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 7bd8b9ed..0f4fdb13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,8 +465,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 ) @@ -479,8 +480,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" ) 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/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/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 35de68ce..ab44eb08 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -56,10 +56,6 @@ void RendererGL::reset() { void RendererGL::initGraphicsContextInternal() { gl.reset(); -#if defined(USING_GLES) || defined(__ANDROID__) - driverInfo.usingGLES = true; -#endif - 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"); @@ -69,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; @@ -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,18 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele ); } } +} + +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 + glLogicOp = [](GLenum) {}; } \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 81a18f30..11970d91 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -117,7 +117,6 @@ std::filesystem::path Emulator::getConfigPath() { #endif void Emulator::step() {} -void Emulator::render() {} // Only resume if a ROM is properly loaded void Emulator::resume() { 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 fe3cb6c4..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; diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 881dc02d..dffe5ca0 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -140,6 +140,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!"); diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp index 25ff576c..919b6694 100644 --- a/src/panda_qt/screen.cpp +++ b/src/panda_qt/screen.cpp @@ -60,11 +60,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 +73,10 @@ bool ScreenWidget::createGLContext() { this->windowInfo = *windowInfo; glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + if (glContext == nullptr) { + return false; + } + glContext->DoneCurrent(); } 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); From 5f4802828470ba01aceceede1f37906103d7c321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 28 Dec 2024 22:19:17 +0000 Subject: [PATCH 09/13] Qt: Fix Wayland support Qt will only create a Wayland surface when show() is called on the main window and on the ScreenWidget. Thus, call the function before creating the GL context. Doesn't cause regressions on XWayland, untested in other platforms. Fixes #586 --- src/panda_qt/main.cpp | 1 - src/panda_qt/main_window.cpp | 3 ++- src/panda_qt/screen.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) 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 dffe5ca0..abb695ca 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -22,6 +22,7 @@ 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); @@ -699,4 +700,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 919b6694..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"); @@ -84,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(); } From 8604a98edf32148a77ed13ca95143c52d7f00a04 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 29 Dec 2024 00:30:31 +0200 Subject: [PATCH 10/13] No need to call screen->show() twice --- src/panda_qt/main_window.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index abb695ca..c060318e 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -28,9 +28,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) 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); From bde51b6d2762982e4bdb4ba73bd27e12db404610 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 29 Dec 2024 13:37:11 +0200 Subject: [PATCH 11/13] Fix disabling Wayland & building on some distros (#700) --- CMakeLists.txt | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4fdb13..ce8d3b92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,16 +427,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() @@ -450,7 +456,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 @@ -759,7 +765,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) From 59f51f7d0cbe2cb46ab033a9fad1514387009d8e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 29 Dec 2024 14:28:04 +0200 Subject: [PATCH 12/13] GLES: Properly stub out logic ops --- src/core/renderer_gl/renderer_gl.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index ab44eb08..c1899655 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -1263,5 +1263,7 @@ void RendererGL::setupGLES() { } // Stub out logic operations so that calling them doesn't crash the emulator - glLogicOp = [](GLenum) {}; -} \ No newline at end of file + if (!glLogicOp) { + glLogicOp = [](GLenum) {}; + } +} From 29083f0a08e6f04dce1146827a3c49c9fb0da7e7 Mon Sep 17 00:00:00 2001 From: Jonian Guveli Date: Sun, 29 Dec 2024 15:36:26 +0200 Subject: [PATCH 13/13] Fix git versioning --- CMakeLists.txt | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce8d3b92..be70036c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,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/)