diff --git a/app/src/main/cpp/AndroidDeviceDetection.cpp b/app/src/main/cpp/AndroidDeviceDetection.cpp new file mode 100644 index 0000000..6fa45b3 --- /dev/null +++ b/app/src/main/cpp/AndroidDeviceDetection.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#ifdef __ANDROID__ + +#include "AndroidDeviceDetection.h" +#include "common/Console.h" +#include +#include + +namespace AndroidDeviceDetection +{ + static std::string GetSystemProperty(const char* key) + { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(key, value) > 0) + return std::string(value); + return ""; + } + + std::string GetManufacturer() + { + return GetSystemProperty("ro.product.manufacturer"); + } + + std::string GetModel() + { + return GetSystemProperty("ro.product.model"); + } + + std::string GetGPURenderer() + { + // This would need to be called from GL/Vulkan context + // For now, we rely on hardware detection + return GetSystemProperty("ro.hardware"); + } + + bool IsMediatek() + { + std::string hardware = GetSystemProperty("ro.hardware"); + std::string board = GetSystemProperty("ro.product.board"); + std::string platform = GetSystemProperty("ro.board.platform"); + + // Convert to lowercase for comparison + auto toLower = [](std::string str) { + for (char& c : str) c = std::tolower(c); + return str; + }; + + hardware = toLower(hardware); + board = toLower(board); + platform = toLower(platform); + + // Check for Mediatek identifiers + return (hardware.find("mt") == 0 || + hardware.find("mediatek") != std::string::npos || + board.find("mt") == 0 || + platform.find("mt") == 0 || + platform.find("mediatek") != std::string::npos); + } + + bool IsSnapdragon() + { + std::string hardware = GetSystemProperty("ro.hardware"); + std::string board = GetSystemProperty("ro.product.board"); + std::string platform = GetSystemProperty("ro.board.platform"); + + auto toLower = [](std::string str) { + for (char& c : str) c = std::tolower(c); + return str; + }; + + hardware = toLower(hardware); + board = toLower(board); + platform = toLower(platform); + + // Check for Qualcomm/Snapdragon identifiers + return (hardware.find("qcom") != std::string::npos || + hardware.find("qualcomm") != std::string::npos || + platform.find("msm") == 0 || + platform.find("sdm") == 0 || + platform.find("sm") == 0 || + platform.find("qcom") != std::string::npos); + } + + GPUVendor DetectGPUVendor() + { + if (IsSnapdragon()) + { + Console.WriteLn("Detected Qualcomm Snapdragon (Adreno GPU)"); + return GPUVendor::Qualcomm; + } + + if (IsMediatek()) + { + Console.WriteLn("Detected Mediatek (Mali GPU)"); + return GPUVendor::ARM; + } + + // Check for other vendors via hardware string + std::string hardware = GetSystemProperty("ro.hardware"); + if (hardware.find("exynos") != std::string::npos) + { + Console.WriteLn("Detected Samsung Exynos (Mali GPU)"); + return GPUVendor::ARM; + } + + Console.WriteLn("Unknown GPU vendor, hardware: %s", hardware.c_str()); + return GPUVendor::Unknown; + } +} + +#endif // __ANDROID__ diff --git a/app/src/main/cpp/AndroidDeviceDetection.h b/app/src/main/cpp/AndroidDeviceDetection.h new file mode 100644 index 0000000..ebfcbd0 --- /dev/null +++ b/app/src/main/cpp/AndroidDeviceDetection.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#ifdef __ANDROID__ + +#include + +namespace AndroidDeviceDetection +{ + enum class GPUVendor + { + Unknown, + Qualcomm, // Adreno (Snapdragon) + ARM, // Mali (Mediatek, Exynos, etc.) + Imagination, // PowerVR + Other + }; + + // Detect GPU vendor from system properties and GL/Vulkan strings + GPUVendor DetectGPUVendor(); + + // Check if device is Mediatek (Mali GPU) + bool IsMediatek(); + + // Check if device is Snapdragon (Adreno GPU) + bool IsSnapdragon(); + + // Get device manufacturer + std::string GetManufacturer(); + + // Get device model + std::string GetModel(); + + // Get GPU renderer string (requires GL context or Vulkan device) + std::string GetGPURenderer(); +} + +#endif // __ANDROID__ diff --git a/app/src/main/cpp/pcsx2/CMakeLists.txt b/app/src/main/cpp/pcsx2/CMakeLists.txt index 1116ca4..89ce027 100644 --- a/app/src/main/cpp/pcsx2/CMakeLists.txt +++ b/app/src/main/cpp/pcsx2/CMakeLists.txt @@ -1127,6 +1127,7 @@ if(ANDROID) ../AchievementsJNI.cpp ../AchievementsAndroid.cpp ../AchievementsNativeMethods.cpp + ../AndroidDeviceDetection.cpp ) elseif(LINUX) target_sources(PCSX2 PRIVATE diff --git a/app/src/main/cpp/pcsx2/GS/GSUtil.cpp b/app/src/main/cpp/pcsx2/GS/GSUtil.cpp index 4e4928d..b641276 100644 --- a/app/src/main/cpp/pcsx2/GS/GSUtil.cpp +++ b/app/src/main/cpp/pcsx2/GS/GSUtil.cpp @@ -6,6 +6,7 @@ #include "GS/GSUtil.h" #include "MultiISA.h" #include "common/StringUtil.h" +#include "common/Console.h" #include @@ -22,6 +23,10 @@ #include #endif +#ifdef __ANDROID__ +#include "AndroidDeviceDetection.h" +#endif + namespace { struct GSUtilMaps { @@ -213,6 +218,58 @@ GSRendererType GSUtil::GetPreferredRenderer() #elif defined(_WIN32) // Use D3D device info to select renderer. preferred_renderer = D3D::GetPreferredRenderer(); +#elif defined(__ANDROID__) + // Android: Detect GPU vendor and choose appropriate renderer + using namespace AndroidDeviceDetection; + GPUVendor vendor = DetectGPUVendor(); + + if (vendor == GPUVendor::ARM) + { + // Mediatek/Mali GPUs: Prefer OpenGL over Vulkan + // Vulkan drivers on Mali are often buggy, especially on Mediatek + // OpenGL works but may have 2D graphics issues that need workarounds + Console.Warning("Mediatek/Mali GPU detected: Using OpenGL renderer (Vulkan has known issues)"); +#if defined(ENABLE_OPENGL) + preferred_renderer = GSRendererType::OGL; +#elif defined(ENABLE_VULKAN) + // Fallback to Vulkan if OpenGL not available (but warn user) + Console.Error("OpenGL not available, falling back to Vulkan (may have issues on Mali)"); + preferred_renderer = GSRendererType::VK; +#else + preferred_renderer = GSRendererType::SW; +#endif + } + else if (vendor == GPUVendor::Qualcomm) + { + // Snapdragon/Adreno GPUs: Prefer Vulkan (good driver support) + Console.WriteLn("Qualcomm/Adreno GPU detected: Using Vulkan renderer"); +#if defined(ENABLE_VULKAN) + preferred_renderer = GSRendererType::VK; +#elif defined(ENABLE_OPENGL) + preferred_renderer = GSRendererType::OGL; +#else + preferred_renderer = GSRendererType::SW; +#endif + } + else + { + // Unknown vendor: Try Vulkan first, then OpenGL + Console.WriteLn("Unknown GPU vendor: Trying Vulkan renderer"); +#if defined(ENABLE_VULKAN) + if (GSDeviceVK::IsSuitableDefaultRenderer()) + preferred_renderer = GSRendererType::VK; +#endif + if (preferred_renderer == GSRendererType::Auto) + { +#if defined(ENABLE_OPENGL) + preferred_renderer = GSRendererType::OGL; +#elif defined(ENABLE_VULKAN) + preferred_renderer = GSRendererType::VK; +#else + preferred_renderer = GSRendererType::SW; +#endif + } + } #else // Linux: Prefer Vulkan if the driver isn't buggy. #if defined(ENABLE_VULKAN) diff --git a/app/src/main/cpp/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/app/src/main/cpp/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 16fa4db..8a19795 100644 --- a/app/src/main/cpp/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/app/src/main/cpp/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -18,9 +18,18 @@ #include "IconsFontAwesome5.h" #include +#include #include #include +// Some texture barrier extension macros may not be generated by GLAD for GLES builds. +#ifndef GLAD_GL_EXT_texture_barrier +#define GLAD_GL_EXT_texture_barrier 0 +#endif +#ifndef GLAD_GL_OES_texture_barrier +#define GLAD_GL_OES_texture_barrier 0 +#endif + static constexpr u32 g_vs_cb_index = 1; static constexpr u32 g_ps_cb_index = 0; @@ -626,24 +635,42 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo) //bool vendor_id_amd = false; bool vendor_id_nvidia = false; //bool vendor_id_intel = false; + bool vendor_id_arm_mali = false; const char* vendor = (const char*)glGetString(GL_VENDOR); - if (std::strstr(vendor, "Advanced Micro Devices") || std::strstr(vendor, "ATI Technologies Inc.") || - std::strstr(vendor, "ATI")) + const char* renderer = (const char*)glGetString(GL_RENDERER); + const std::string vendor_str = vendor ? vendor : ""; + const std::string renderer_str = renderer ? renderer : ""; + const auto to_lower = [](std::string s) { + for (char& c : s) + c = static_cast(std::tolower(static_cast(c))); + return s; + }; + const std::string vendor_lower = to_lower(vendor_str); + const std::string renderer_lower = to_lower(renderer_str); + + if (vendor_lower.find("advanced micro devices") != std::string::npos || + vendor_lower.find("ati technologies inc.") != std::string::npos || + vendor_lower.find("ati") != std::string::npos) { Console.WriteLn(Color_StrongRed, "GL: AMD GPU detected."); //vendor_id_amd = true; } - else if (std::strstr(vendor, "NVIDIA Corporation")) + else if (vendor_lower.find("nvidia corporation") != std::string::npos || vendor_lower.find("nvidia") != std::string::npos) { Console.WriteLn(Color_StrongGreen, "GL: NVIDIA GPU detected."); vendor_id_nvidia = true; } - else if (std::strstr(vendor, "Intel")) + else if (vendor_lower.find("intel") != std::string::npos) { Console.WriteLn(Color_StrongBlue, "GL: Intel GPU detected."); //vendor_id_intel = true; } + else if (vendor_lower.find("arm") != std::string::npos || renderer_lower.find("mali") != std::string::npos) + { + Console.Warning("GL: ARM Mali GPU detected - applying workarounds for 2D graphics issues."); + vendor_id_arm_mali = true; + } GLint major_gl = 0; GLint minor_gl = 0; @@ -724,7 +751,7 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo) if (!m_is_gles) { buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage; } else { - buggy_pbo = GLAD_GL_EXT_buffer_storage; + buggy_pbo = !GLAD_GL_EXT_buffer_storage; } if (buggy_pbo) Console.Warning("GL: Not using PBOs for texture uploads because buffer_storage is unavailable."); @@ -747,12 +774,14 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo) m_features.framebuffer_fetch = false; } + const bool has_texture_barrier = + GLAD_GL_ARB_texture_barrier || GLAD_GL_EXT_texture_barrier || GLAD_GL_OES_texture_barrier; if (GSConfig.OverrideTextureBarriers == 0) m_features.texture_barrier = m_features.framebuffer_fetch; // Force Disabled else if (GSConfig.OverrideTextureBarriers == 1) m_features.texture_barrier = true; // Force Enabled else - m_features.texture_barrier = m_features.framebuffer_fetch || GLAD_GL_ARB_texture_barrier; + m_features.texture_barrier = m_features.framebuffer_fetch || has_texture_barrier; if (!m_features.texture_barrier) { // OSD message disabled: GL_ARB_texture_barrier warning @@ -801,6 +830,54 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo) m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"), m_features.vs_expand ? "vertex expanding" : "CPU"); + // Mali GPU workarounds for missing 2D graphics + if (vendor_id_arm_mali) + { + Console.Warning("GL: Applying Mali GPU workarounds:"); + + // Mali has issues with framebuffer fetch - disable it + if (m_features.framebuffer_fetch) + { + Console.Warning(" - Disabling framebuffer fetch (causes 2D graphics issues on Mali)"); + m_features.framebuffer_fetch = false; + } + + // Force texture barriers on Mali + if (!m_features.texture_barrier && has_texture_barrier) + { + Console.Warning(" - Enabling texture barriers (required for proper blending on Mali)"); + m_features.texture_barrier = true; + } + else if (!has_texture_barrier) + { + // Keep barriers disabled so we fall back to safe RT copies instead of no-op barriers. + m_features.texture_barrier = false; + } + + // Disable vertex shader expansion on Mali (can cause rendering issues) + if (m_features.vs_expand) + { + Console.Warning(" - Disabling vertex shader expansion (unstable on Mali)"); + m_features.vs_expand = false; + } + + // Force PBO usage off on Mali (can cause texture corruption) + if (!buggy_pbo) + { + Console.Warning(" - Disabling PBO for texture uploads (causes corruption on Mali)"); + buggy_pbo = true; + } + + // Disable download PBO as well + if (!m_disable_download_pbo) + { + Console.Warning(" - Disabling PBO for texture downloads (causes corruption on Mali)"); + m_disable_download_pbo = true; + } + + Console.Warning("GL: Mali workarounds applied. If 2D graphics still missing, try Software renderer."); + } + return true; } @@ -1368,10 +1445,14 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type else header += "#define HAS_FRAMEBUFFER_FETCH 0\n"; - if (GLAD_GL_ARB_clip_control) - header += "#define HAS_CLIP_CONTROL 1\n"; - else - header += "#define HAS_CLIP_CONTROL 0\n"; + if (GLAD_GL_ARB_clip_control) + { + header += "#define HAS_CLIP_CONTROL 1\n"; + } + else + { + header += "#define HAS_CLIP_CONTROL 0\n"; + } // Allow to puts several shader in 1 files switch (type)