diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index a0f17daaf2..e75719e53a 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -220,7 +220,9 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem } bool VulkanContext::InitObjects() { - InitQueue(); + if (!InitQueue()) { + return false; + } if (!InitSwapchain()) { return false; @@ -616,7 +618,7 @@ void VulkanContext::ReinitSurfaceAndroid(int width, int height) { } #endif -void VulkanContext::InitQueue() { +bool VulkanContext::InitQueue() { // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = new VkBool32[queue_count]; for (uint32_t i = 0; i < queue_count; i++) { @@ -654,8 +656,8 @@ void VulkanContext::InitQueue() { // Generate error if could not find both a graphics and a present queue if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) { - std::cout << "Could not find a graphics and a present queue"; - exit(-1); + ELOG("Could not find a graphics and a present queue"); + return false; } graphics_queue_family_index_ = graphicsQueueNodeIndex; @@ -664,9 +666,15 @@ void VulkanContext::InitQueue() { uint32_t formatCount; VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, nullptr); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) + return false; VkSurfaceFormatKHR *surfFormats = new VkSurfaceFormatKHR[formatCount]; res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, surfFormats); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) { + delete[] surfFormats; + return false; + } // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. @@ -695,6 +703,7 @@ void VulkanContext::InitQueue() { vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_); ILOG("gfx_queue_: %p", gfx_queue_); + return true; } bool VulkanContext::InitSwapchain() { diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 6dbef6e33b..476edfcf10 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -141,7 +141,7 @@ public: void InitSurfaceAndroid(ANativeWindow *native_window, int width, int height); void ReinitSurfaceAndroid(int width, int height); #endif - void InitQueue(); + bool InitQueue(); bool InitObjects(); bool InitSwapchain(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7579c65aaa..68cb2d71bf 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -177,7 +177,7 @@ void GameSettingsScreen::CreateViews() { tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll); graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode"))); - static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan (experimental)" }; + static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" }; PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, GPU_BACKEND_OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager())); renderingBackendChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend); #if !PPSSPP_PLATFORM(WINDOWS) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 01a353cec8..c120e5cd1d 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -56,6 +56,7 @@ #include "base/stringutil.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" #include "util/text/parsers.h" #include "Windows/GPU/WindowsVulkanContext.h" @@ -204,6 +205,12 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m bool success = draw_->CreatePresets(); assert(success); // Doesn't fail, we include the compiler. draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + if (!renderManager->HasBackbuffers()) { + Shutdown(); + return false; + } return true; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index d1260fce3c..76a499bf51 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -38,6 +38,7 @@ #include "gfx_es2/gpu_features.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" #include "Core/Config.h" #include "Core/Loaders.h" #include "Core/System.h" @@ -326,13 +327,29 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, 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); } - g_Vulkan->InitObjects(); - draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); - bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. - assert(success); - draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - ILOG("AndroidVulkanContext::Init completed"); - return true; + + bool success = true; + if (g_Vulkan->InitObjects()) { + draw_ = Draw::T3DCreateVulkanContext(g_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() { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index c29b3294e8..4f265e8f81 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -133,9 +133,13 @@ void VulkanRenderManager::CreateBackbuffers() { VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); assert(res == VK_SUCCESS); - VkImage* swapchainImages = new VkImage[swapchainImageCount_]; + VkImage *swapchainImages = new VkImage[swapchainImageCount_]; res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) { + delete[] swapchainImages; + return; + } VkCommandBuffer cmdInit = GetInitCmd(); @@ -172,15 +176,19 @@ void VulkanRenderManager::CreateBackbuffers() { } delete[] swapchainImages; - InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. - InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + // Must be before InitBackbufferRenderPass. + if (InitDepthStencilBuffer(cmdInit)) { + InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + } curWidth_ = -1; curHeight_ = -1; - VLOG("Backbuffers Created"); + if (HasBackbuffers()) { + VLOG("Backbuffers Created"); + } // Start the thread. - if (useThread) { + if (useThread && HasBackbuffers()) { run_ = true; // Won't necessarily be 0. threadInitFrame_ = vulkan_->GetCurFrame(); @@ -453,8 +461,8 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int queueRunner_.CopyReadbackBuffer(w, h, destFormat, destFormat, pixelStride, pixels); } -void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { - VkResult U_ASSERT_ONLY res; +bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { + VkResult res; // We share the same depth buffer but have multiple color buffers, see the loop below. VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; @@ -473,12 +481,18 @@ void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { attachments[0] = swapchainImages_[i].view; res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) { + framebuffers_.clear(); + return false; + } } + + return true; } -void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - bool U_ASSERT_ONLY pass; +bool VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { + VkResult res; + bool pass; const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; @@ -506,8 +520,10 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { depth_.format = depth_format; VkDevice device = vulkan_->GetDevice(); - res = vkCreateImage(device, &image_info, NULL, &depth_.image); + res = vkCreateImage(device, &image_info, nullptr, &depth_.image); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) + return false; vkGetImageMemoryRequirements(device, depth_.image, &mem_reqs); @@ -517,12 +533,18 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { 0, /* No requirements */ &mem_alloc.memoryTypeIndex); assert(pass); + if (!pass) + return false; res = vkAllocateMemory(device, &mem_alloc, NULL, &depth_.mem); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) + return false; res = vkBindImageMemory(device, depth_.image, depth_.mem, 0); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) + return false; TransitionImageLayout2(cmd, depth_.image, aspectMask, @@ -547,6 +569,10 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view); assert(res == VK_SUCCESS); + if (res != VK_SUCCESS) + return false; + + return true; } void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 818a44f41b..0f9995771c 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -204,9 +204,13 @@ public: void CreateBackbuffers(); void DestroyBackbuffers(); + bool HasBackbuffers() { + return !framebuffers_.empty(); + } + private: - void InitBackbufferFramebuffers(int width, int height); - void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + bool InitBackbufferFramebuffers(int width, int height); + bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); void Submit(int frame, bool triggerFence);