diff --git a/CMakeLists.txt b/CMakeLists.txt index d8cdbee3a4..1a1ae75c2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -660,6 +660,13 @@ if(ANDROID) set(nativeExtra ${nativeExtra} ext/native/base/NativeApp.h android/jni/app-android.cpp + android/jni/AndroidEGLContext.cpp + android/jni/AndroidEGLContext.h + android/jni/AndroidJavaGLContext.cpp + android/jni/AndroidJavaGLContext.h + android/jni/AndroidVulkanContext.cpp + android/jni/AndroidVulkanContext.h + android/jni/AndroidGraphicsContext.h android/jni/native_audio.cpp android/jni/native_audio.h android/jni/native-audio-so.cpp diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 70448a0905..6f3cfb5d55 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -6,9 +6,11 @@ #include #include "base/basictypes.h" +#include "base/display.h" #include "VulkanContext.h" #include "GPU/Common/ShaderCommon.h" #include "Common/StringUtils.h" +#include "Core/Config.h" // Change this to 1, 2, and 3 to fake failures in a few places, so that // we can test our fallback-to-GL code. diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 1c51566224..d570aa8ce5 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -722,8 +722,14 @@ void NativeShutdownGraphics() { ui_draw2d.Shutdown(); ui_draw2d_front.Shutdown(); - colorPipeline->Release(); - texColorPipeline->Release(); + if (colorPipeline) { + colorPipeline->Release(); + colorPipeline = nullptr; + } + if (texColorPipeline) { + texColorPipeline->Release(); + texColorPipeline = nullptr; + } ILOG("NativeShutdownGraphics done"); } diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index ea471b88fb..9e95c6a1b8 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -278,6 +278,24 @@ + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + true true @@ -435,6 +453,30 @@ + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 7aa93096ef..8cce6b63ec 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -200,6 +200,15 @@ Other Platforms + + Other Platforms + + + Other Platforms + + + Other Platforms + @@ -368,6 +377,18 @@ Other Platforms + + Other Platforms + + + Other Platforms + + + Other Platforms + + + Other Platforms + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 5ea6ccd057..ecd4352c45 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -406,6 +406,9 @@ LOCAL_STATIC_LIBRARIES += ppsspp_core LOCAL_MODULE := ppsspp_jni LOCAL_SRC_FILES := \ $(SRC)/android/jni/app-android.cpp \ + $(SRC)/android/jni/AndroidEGLContext.cpp \ + $(SRC)/android/jni/AndroidJavaGLContext.cpp \ + $(SRC)/android/jni/AndroidVulkanContext.cpp \ $(SRC)/android/jni/native_audio.cpp \ $(SRC)/android/jni/native-audio-so.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp new file mode 100644 index 0000000000..8742085535 --- /dev/null +++ b/android/jni/AndroidEGLContext.cpp @@ -0,0 +1,62 @@ +#include "base/logging.h" +#include "base/NativeApp.h" +#include "gfx_es2/gpu_features.h" + +#include "AndroidEGLContext.h" +#include "GL/GLInterface/EGLAndroid.h" +#include "Core/System.h" + +bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, int backbufferHeight, int backbufferFormat, int androidVersion) { + wnd_ = wnd; + gl = HostGL_CreateGLInterface(); + if (!gl) { + ELOG("ERROR: Failed to create GL interface"); + return false; + } + ILOG("EGL interface created. Desired backbuffer size: %dx%d", backbufferWidth, backbufferHeight); + + // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... + gl->SetBackBufferDimensions(backbufferWidth, backbufferHeight); + gl->SetMode(MODE_DETECT_ES); + + bool use565 = false; + + // This workaround seems only be needed on some really old devices. + if (androidVersion < ANDROID_VERSION_ICS) { + switch (backbufferFormat) { + case 4: // PixelFormat.RGB_565 + use565 = true; + break; + default: + break; + } + } + + if (!gl->Create(wnd, false, use565)) { + ELOG("EGL creation failed! (use565=%d)", (int)use565); + // TODO: What do we do now? + delete gl; + return false; + } + gl->MakeCurrent(); + CheckGLExtensions(); + draw_ = Draw::T3DCreateGLContext(); + SetGPUBackend(GPUBackend::OPENGL); + bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these. + assert(success); + return true; +} + +void AndroidEGLGraphicsContext::Shutdown() { + delete draw_; + draw_ = nullptr; + NativeShutdownGraphics(); + gl->ClearCurrent(); + gl->Shutdown(); + delete gl; + ANativeWindow_release(wnd_); +} + +void AndroidEGLGraphicsContext::SwapBuffers() { + gl->Swap(); +} diff --git a/android/jni/AndroidEGLContext.h b/android/jni/AndroidEGLContext.h new file mode 100644 index 0000000000..efeafecf80 --- /dev/null +++ b/android/jni/AndroidEGLContext.h @@ -0,0 +1,22 @@ +#pragma once + +#include "AndroidGraphicsContext.h" +#include "Common/GL/GLInterfaceBase.h" + +class AndroidEGLGraphicsContext : public AndroidGraphicsContext { +public: + AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {} + bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; + void Shutdown() override; + void SwapBuffers() override; + void SwapInterval(int interval) override {} + void Resize() override {} + Draw::DrawContext *GetDrawContext() override { + return draw_; + } + +private: + Draw::DrawContext *draw_; + ANativeWindow *wnd_; + cInterfaceBase *gl; +}; diff --git a/android/jni/AndroidGraphicsContext.h b/android/jni/AndroidGraphicsContext.h new file mode 100644 index 0000000000..027be92505 --- /dev/null +++ b/android/jni/AndroidGraphicsContext.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "thin3d/thin3d.h" + +#include "Common/GraphicsContext.h" + +enum { + ANDROID_VERSION_GINGERBREAD = 9, + ANDROID_VERSION_ICS = 14, + ANDROID_VERSION_JELLYBEAN = 16, + ANDROID_VERSION_KITKAT = 19, + ANDROID_VERSION_LOLLIPOP = 21, + ANDROID_VERSION_MARSHMALLOW = 23, + ANDROID_VERSION_NOUGAT = 24, + ANDROID_VERSION_NOUGAT_1 = 25, +}; + +class AndroidGraphicsContext : public GraphicsContext { +public: + virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0; +}; diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp new file mode 100644 index 0000000000..8a97a62187 --- /dev/null +++ b/android/jni/AndroidJavaGLContext.cpp @@ -0,0 +1,19 @@ +#include "AndroidJavaGLContext.h" +#include "base/NativeApp.h" +#include "gfx_es2/gpu_features.h" +#include "Core/System.h" + +AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() { + CheckGLExtensions(); + draw_ = Draw::T3DCreateGLContext(); + SetGPUBackend(GPUBackend::OPENGL); + bool success = draw_->CreatePresets(); + assert(success); +} + +void AndroidJavaEGLGraphicsContext::Shutdown() { + ILOG("AndroidJavaEGLGraphicsContext::Shutdown"); + delete draw_; + draw_ = nullptr; + NativeShutdownGraphics(); +} diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h new file mode 100644 index 0000000000..36e443e51d --- /dev/null +++ b/android/jni/AndroidJavaGLContext.h @@ -0,0 +1,22 @@ +#pragma once + +#include "AndroidGraphicsContext.h" + +// Doesn't do much. Just to fit in. +class AndroidJavaEGLGraphicsContext : public GraphicsContext { +public: + AndroidJavaEGLGraphicsContext(); + ~AndroidJavaEGLGraphicsContext() { + delete draw_; + } + void Shutdown() override; + void SwapBuffers() override {} + void SwapInterval(int interval) override {} + void Resize() override {} + Draw::DrawContext *GetDrawContext() override { + return draw_; + } +private: + Draw::DrawContext *draw_; +}; + diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp new file mode 100644 index 0000000000..99b21469be --- /dev/null +++ b/android/jni/AndroidVulkanContext.cpp @@ -0,0 +1,198 @@ +#include "AndroidVulkanContext.h" +#include "base/NativeApp.h" +#include "base/display.h" +#include "Common/Vulkan/VulkanLoader.h" +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/VulkanRenderManager.h" +#include "util/text/parsers.h" +#include "Core/Config.h" +#include "Core/System.h" + +static VulkanLogOptions g_LogOptions; + +const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) { + switch (type) { + case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance"; + case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice"; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device"; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue"; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer"; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory"; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer"; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView"; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image"; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView"; + case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule"; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline"; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout"; + case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler"; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet"; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout"; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool"; + case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence"; + case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore"; + case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event"; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool"; + case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer"; + case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass"; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache"; + case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR"; + case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR"; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool"; + default: return "Unknown"; + } +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) { + const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData; + int loglevel = ANDROID_LOG_INFO; + if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + loglevel = ANDROID_LOG_ERROR; + } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + loglevel = ANDROID_LOG_WARN; + } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) { + loglevel = ANDROID_LOG_WARN; + } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { + loglevel = ANDROID_LOG_WARN; + } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { + loglevel = ANDROID_LOG_WARN; + } + + __android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg); + + // false indicates that layer should not bail-out of an + // API call that had validation failures. This may mean that the + // app dies inside the driver due to invalid parameter(s). + // That's what would happen without validation layers, so we'll + // keep that behavior here. + return false; +} + +AndroidVulkanContext::~AndroidVulkanContext() { + delete g_Vulkan; + g_Vulkan = nullptr; +} + +bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) { + ILOG("AndroidVulkanContext::Init"); + init_glslang(); + + g_LogOptions.breakOnError = true; + g_LogOptions.breakOnWarning = true; + g_LogOptions.msgBoxOnError = false; + + ILOG("Creating Vulkan context"); + Version gitVer(PPSSPP_GIT_VERSION); + + if (!g_Vulkan) { + g_Vulkan = new VulkanContext(); + } + VulkanContext::CreateInfo info{}; + info.app_name = "PPSSPP"; + info.app_ver = gitVer.ToInteger(); + info.flags = VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED; + if (VK_SUCCESS != g_Vulkan->CreateInstance(info)) { + ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str()); + System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead."); + delete g_Vulkan; + g_Vulkan = nullptr; + return false; + } + + int physicalDevice = g_Vulkan->GetBestPhysicalDevice(); + if (physicalDevice < 0) { + ELOG("No usable Vulkan device found."); + g_Vulkan->DestroyInstance(); + delete g_Vulkan; + g_Vulkan = nullptr; + return false; + } + + g_Vulkan->ChooseDevice(physicalDevice); + // Here we can enable device extensions if we like. + + ILOG("Creating Vulkan device"); + if (g_Vulkan->CreateDevice() != VK_SUCCESS) { + ILOG("Failed to create vulkan device: %s", g_Vulkan->InitError().c_str()); + System_SendMessage("toast", "No Vulkan driver found. Using OpenGL instead."); + g_Vulkan->DestroyInstance(); + delete g_Vulkan; + g_Vulkan = nullptr; + return false; + } + int width = desiredBackbufferSizeX; + int height = desiredBackbufferSizeY; + if (!width || !height) { + width = pixel_xres; + height = pixel_yres; + } + ILOG("InitSurfaceAndroid: width=%d height=%d", width, height); + g_Vulkan->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd, nullptr, width, height); + if (g_validate_) { + int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); + } + + bool success = true; + if (g_Vulkan->InitObjects()) { + draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bGfxDebugSplitSubmit); + SetGPUBackend(GPUBackend::VULKAN); + success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. + assert(success); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + success = renderManager->HasBackbuffers(); + } else { + success = false; + } + + ILOG("AndroidVulkanContext::Init completed, %s", success ? "successfully" : "but failed"); + if (!success) { + g_Vulkan->DestroyObjects(); + g_Vulkan->DestroyDevice(); + g_Vulkan->DestroyDebugMsgCallback(); + + g_Vulkan->DestroyInstance(); + } + return success; +} + +void AndroidVulkanContext::Shutdown() { + ILOG("AndroidVulkanContext::Shutdown"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + ILOG("Calling NativeShutdownGraphics"); + NativeShutdownGraphics(); + delete draw_; + draw_ = nullptr; + g_Vulkan->WaitUntilQueueIdle(); + g_Vulkan->DestroyObjects(); + g_Vulkan->DestroyDevice(); + g_Vulkan->DestroyDebugMsgCallback(); + + g_Vulkan->DestroyInstance(); + + // We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app. + + finalize_glslang(); + ILOG("AndroidVulkanContext::Shutdown completed"); +} + +void AndroidVulkanContext::SwapBuffers() { +} + +void AndroidVulkanContext::Resize() { + ILOG("AndroidVulkanContext::Resize begin (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + g_Vulkan->DestroyObjects(); + + // backbufferResize updated these values. TODO: Notify another way? + g_Vulkan->ReinitSurface(pixel_xres, pixel_yres); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + ILOG("AndroidVulkanContext::Resize end (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); +} + +void AndroidVulkanContext::SwapInterval(int interval) { +} diff --git a/android/jni/AndroidVulkanContext.h b/android/jni/AndroidVulkanContext.h new file mode 100644 index 0000000000..8c48e09c92 --- /dev/null +++ b/android/jni/AndroidVulkanContext.h @@ -0,0 +1,34 @@ +#pragma once + +#include "AndroidGraphicsContext.h" + +static const bool g_validate_ = true; + +class VulkanContext; + +class AndroidVulkanContext : public AndroidGraphicsContext { +public: + AndroidVulkanContext() : draw_(nullptr) {} + ~AndroidVulkanContext(); + + bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; + void Shutdown() override; + void SwapInterval(int interval) override; + void SwapBuffers() override; + void Resize() override; + + void *GetAPIContext() override { return g_Vulkan; } + + Draw::DrawContext *GetDrawContext() override { + return draw_; + } +private: + VulkanContext *g_Vulkan = nullptr; + Draw::DrawContext *draw_ = nullptr; +}; + +struct VulkanLogOptions { + bool breakOnWarning; + bool breakOnError; + bool msgBoxOnError; +}; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index a06e2125f5..b5ef7779a2 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -4,7 +4,6 @@ // It calls a set of methods defined in NativeApp.h. These should be implemented // by your game or app. - #include #include #include @@ -18,6 +17,11 @@ #include #include +#include "AndroidGraphicsContext.h" +#include "AndroidVulkanContext.h" +#include "AndroidEGLContext.h" +#include "AndroidJavaGLContext.h" + #include "base/basictypes.h" #include "base/stringutil.h" #include "base/display.h" @@ -58,17 +62,6 @@ JavaVM *javaVM; static AndroidAudioState *g_audioState; -enum { - ANDROID_VERSION_GINGERBREAD = 9, - ANDROID_VERSION_ICS = 14, - ANDROID_VERSION_JELLYBEAN = 16, - ANDROID_VERSION_KITKAT = 19, - ANDROID_VERSION_LOLLIPOP = 21, - ANDROID_VERSION_MARSHMALLOW = 23, - ANDROID_VERSION_NOUGAT = 24, - ANDROID_VERSION_NOUGAT_1 = 25, -}; - struct FrameCommand { FrameCommand() {} FrameCommand(std::string cmd, std::string prm) : command(cmd), params(prm) {} @@ -77,331 +70,6 @@ struct FrameCommand { std::string params; }; -class AndroidGraphicsContext : public GraphicsContext { -public: - virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0; -}; - -class AndroidEGLGraphicsContext : public AndroidGraphicsContext { -public: - AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {} - bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; - void Shutdown() override; - void SwapBuffers() override; - void SwapInterval(int interval) override {} - void Resize() override {} - Draw::DrawContext *GetDrawContext() override { - return draw_; - } - -private: - Draw::DrawContext *draw_; - ANativeWindow *wnd_; - cInterfaceBase *gl; -}; - -bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, int backbufferHeight, int backbufferFormat, int androidVersion) { - wnd_ = wnd; - gl = HostGL_CreateGLInterface(); - if (!gl) { - ELOG("ERROR: Failed to create GL interface"); - return false; - } - ILOG("EGL interface created. Desired backbuffer size: %dx%d", backbufferWidth, backbufferHeight); - - // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... - gl->SetBackBufferDimensions(backbufferWidth, backbufferHeight); - gl->SetMode(MODE_DETECT_ES); - - bool use565 = false; - - // This workaround seems only be needed on some really old devices. - if (androidVersion < ANDROID_VERSION_ICS) { - switch (backbufferFormat) { - case 4: // PixelFormat.RGB_565 - use565 = true; - break; - default: - break; - } - } - - if (!gl->Create(wnd, false, use565)) { - ELOG("EGL creation failed! (use565=%d)", (int)use565); - // TODO: What do we do now? - delete gl; - return false; - } - gl->MakeCurrent(); - CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); - SetGPUBackend(GPUBackend::OPENGL); - bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these. - assert(success); - return true; -} - -void AndroidEGLGraphicsContext::Shutdown() { - delete draw_; - draw_ = nullptr; - NativeShutdownGraphics(); - gl->ClearCurrent(); - gl->Shutdown(); - delete gl; - ANativeWindow_release(wnd_); - finalize_glslang(); -} - -void AndroidEGLGraphicsContext::SwapBuffers() { - gl->Swap(); -} - -// Doesn't do much. Just to fit in. -class AndroidJavaEGLGraphicsContext : public GraphicsContext { -public: - AndroidJavaEGLGraphicsContext() { - CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); - SetGPUBackend(GPUBackend::OPENGL); - bool success = draw_->CreatePresets(); - assert(success); - } - ~AndroidJavaEGLGraphicsContext() { - delete draw_; - } - void Shutdown() override { - ILOG("AndroidJavaEGLGraphicsContext::Shutdown"); - delete draw_; - draw_ = nullptr; - NativeShutdownGraphics(); - finalize_glslang(); - } - void SwapBuffers() override {} - void SwapInterval(int interval) override {} - void Resize() override {} - Draw::DrawContext *GetDrawContext() override { - return draw_; - } -private: - Draw::DrawContext *draw_; -}; - - -static const bool g_validate_ = true; -static VulkanContext *g_Vulkan; - -class AndroidVulkanContext : public AndroidGraphicsContext { -public: - AndroidVulkanContext() : draw_(nullptr) {} - ~AndroidVulkanContext() { - delete g_Vulkan; - g_Vulkan = nullptr; - } - - bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; - void Shutdown() override; - void SwapInterval(int interval) override; - void SwapBuffers() override; - void Resize() override; - - void *GetAPIContext() override { return g_Vulkan; } - - Draw::DrawContext *GetDrawContext() override { - return draw_; - } -private: - Draw::DrawContext *draw_; -}; - -struct VulkanLogOptions { - bool breakOnWarning; - bool breakOnError; - bool msgBoxOnError; -}; -static VulkanLogOptions g_LogOptions; - -const char *ObjTypeToString(VkDebugReportObjectTypeEXT type) { - switch (type) { - case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance"; - case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice"; - case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device"; - case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue"; - case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer"; - case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory"; - case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer"; - case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView"; - case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image"; - case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView"; - case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule"; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline"; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout"; - case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler"; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet"; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout"; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool"; - case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence"; - case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore"; - case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event"; - case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool"; - case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer"; - case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass"; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache"; - case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR"; - case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapChainKHR"; - case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "CommandPool"; - default: return "Unknown"; - } -} - -static VKAPI_ATTR VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void *pUserData) { - const VulkanLogOptions *options = (const VulkanLogOptions *)pUserData; - int loglevel = ANDROID_LOG_INFO; - if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - loglevel = ANDROID_LOG_ERROR; - } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - loglevel = ANDROID_LOG_WARN; - } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) { - loglevel = ANDROID_LOG_WARN; - } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { - loglevel = ANDROID_LOG_WARN; - } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - loglevel = ANDROID_LOG_WARN; - } - - __android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg); - - // false indicates that layer should not bail-out of an - // API call that had validation failures. This may mean that the - // app dies inside the driver due to invalid parameter(s). - // That's what would happen without validation layers, so we'll - // keep that behavior here. - return false; -} - -bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) { - ILOG("AndroidVulkanContext::Init"); - init_glslang(); - - g_LogOptions.breakOnError = true; - g_LogOptions.breakOnWarning = true; - g_LogOptions.msgBoxOnError = false; - - ILOG("Creating Vulkan context"); - Version gitVer(PPSSPP_GIT_VERSION); - - if (!g_Vulkan) { - g_Vulkan = new VulkanContext(); - } - VulkanContext::CreateInfo info{}; - info.app_name = "PPSSPP"; - info.app_ver = gitVer.ToInteger(); - info.flags = VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED; - if (VK_SUCCESS != g_Vulkan->CreateInstance(info)) { - ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str()); - System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead."); - delete g_Vulkan; - g_Vulkan = nullptr; - return false; - } - - int physicalDevice = g_Vulkan->GetBestPhysicalDevice(); - if (physicalDevice < 0) { - ELOG("No usable Vulkan device found."); - g_Vulkan->DestroyInstance(); - delete g_Vulkan; - g_Vulkan = nullptr; - return false; - } - - g_Vulkan->ChooseDevice(physicalDevice); - // Here we can enable device extensions if we like. - - ILOG("Creating Vulkan device"); - if (g_Vulkan->CreateDevice() != VK_SUCCESS) { - ILOG("Failed to create vulkan device: %s", g_Vulkan->InitError().c_str()); - System_SendMessage("toast", "No Vulkan driver found. Using OpenGL instead."); - g_Vulkan->DestroyInstance(); - delete g_Vulkan; - g_Vulkan = nullptr; - return false; - } - int width = desiredBackbufferSizeX; - int height = desiredBackbufferSizeY; - if (!width || !height) { - width = pixel_xres; - height = pixel_yres; - } - ILOG("InitSurfaceAndroid: width=%d height=%d", width, height); - g_Vulkan->InitSurface(WINDOWSYSTEM_ANDROID, (void *)wnd, nullptr, width, height); - if (g_validate_) { - int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); - } - - bool success = true; - if (g_Vulkan->InitObjects()) { - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, g_Config.bGfxDebugSplitSubmit); - SetGPUBackend(GPUBackend::VULKAN); - success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. - assert(success); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - - VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - success = renderManager->HasBackbuffers(); - } else { - success = false; - } - - ILOG("AndroidVulkanContext::Init completed, %s", success ? "successfully" : "but failed"); - if (!success) { - g_Vulkan->DestroyObjects(); - g_Vulkan->DestroyDevice(); - g_Vulkan->DestroyDebugMsgCallback(); - - g_Vulkan->DestroyInstance(); - } - return success; -} - -void AndroidVulkanContext::Shutdown() { - ILOG("AndroidVulkanContext::Shutdown"); - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - ILOG("Calling NativeShutdownGraphics"); - NativeShutdownGraphics(); - delete draw_; - draw_ = nullptr; - g_Vulkan->WaitUntilQueueIdle(); - g_Vulkan->DestroyObjects(); - g_Vulkan->DestroyDevice(); - g_Vulkan->DestroyDebugMsgCallback(); - - g_Vulkan->DestroyInstance(); - - // We keep the g_Vulkan context around to avoid invalidating a ton of pointers around the app. - - finalize_glslang(); - ILOG("AndroidVulkanContext::Shutdown completed"); -} - -void AndroidVulkanContext::SwapBuffers() { -} - -void AndroidVulkanContext::Resize() { - ILOG("AndroidVulkanContext::Resize begin (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - - draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - g_Vulkan->DestroyObjects(); - - // backbufferResize updated these values. TODO: Notify another way? - g_Vulkan->ReinitSurface(pixel_xres, pixel_yres); - g_Vulkan->InitObjects(); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - ILOG("AndroidVulkanContext::Resize end (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); -} - -void AndroidVulkanContext::SwapInterval(int interval) { -} - static std::mutex frameCommandLock; static std::queue frameCommands;