#include <crtdbg.h>
#include <sstream>
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/System.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/thin3d_create.h"
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
#include "Common/Data/Text/Parsers.h"
#include "Windows/GPU/WindowsVulkanContext.h"
#ifdef _DEBUG
static const bool g_validate_ = true;
#else
static const bool g_validate_ = false;
#endif
static VulkanInitFlags FlagsFromConfig() {
VulkanInitFlags flags = g_Config.bVSync ? VulkanInitFlags::PRESENT_FIFO : VulkanInitFlags::PRESENT_MAILBOX;
if (g_validate_) {
flags |= VulkanInitFlags::VALIDATE;
}
if (g_Config.bVulkanDisableImplicitLayers) {
flags |= VulkanInitFlags::DISABLE_IMPLICIT_LAYERS;
}
return flags;
}
bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) {
*error_message = "N/A";
if (vulkan_) {
*error_message = "Already initialized";
return false;
}
init_glslang();
g_LogOptions.breakOnError = true;
g_LogOptions.breakOnWarning = true;
g_LogOptions.msgBoxOnError = false;
Version gitVer(PPSSPP_GIT_VERSION);
std::string errorStr;
if (!VulkanLoad(&errorStr)) {
*error_message = "Failed to load Vulkan driver library: ";
(*error_message) += errorStr;
return false;
}
vulkan_ = new VulkanContext();
VulkanContext::CreateInfo info{};
info.app_name = "PPSSPP";
info.app_ver = gitVer.ToInteger();
info.flags = FlagsFromConfig();
if (VK_SUCCESS != vulkan_->CreateInstance(info)) {
*error_message = vulkan_->InitError();
delete vulkan_;
vulkan_ = nullptr;
return false;
}
int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice);
if (deviceNum < 0) {
deviceNum = vulkan_->GetBestPhysicalDevice();
if (!g_Config.sVulkanDevice.empty())
g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName;
}
if (vulkan_->CreateDevice(deviceNum) != VK_SUCCESS) {
*error_message = vulkan_->InitError();
delete vulkan_;
vulkan_ = nullptr;
return false;
}
vulkan_->InitSurface(WINDOWSYSTEM_WIN32, (void *)hInst, (void *)hWnd);
if (!vulkan_->InitSwapchain()) {
*error_message = vulkan_->InitError();
Shutdown();
return false;
}
bool useMultiThreading = g_Config.bRenderMultiThreading;
if (g_Config.iInflightFrames == 1) {
useMultiThreading = false;
}
draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading);
SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName);
bool success = draw_->CreatePresets();
_assert_msg_(success, "Failed to compile preset shaders");
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
if (!renderManager_->HasBackbuffers()) {
Shutdown();
return false;
}
return true;
}
void WindowsVulkanContext::Shutdown() {
if (draw_)
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
delete draw_;
draw_ = nullptr;
vulkan_->WaitUntilQueueIdle();
vulkan_->DestroySwapchain();
vulkan_->DestroySurface();
vulkan_->DestroyDevice();
vulkan_->DestroyInstance();
delete vulkan_;
vulkan_ = nullptr;
renderManager_ = nullptr;
finalize_glslang();
}
void WindowsVulkanContext::Resize() {
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
vulkan_->DestroySwapchain();
vulkan_->UpdateInitFlags(FlagsFromConfig());
vulkan_->InitSwapchain();
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
}
void WindowsVulkanContext::Poll() {
if (vulkan_->IsSwapchainInited() && renderManager_->NeedsSwapchainRecreate()) {
Resize();
} else if (vulkan_->IsSwapchainInited() && windowRestored_) {
Resize();
windowRestored_ = false;
}
}
void *WindowsVulkanContext::GetAPIContext() {
return vulkan_;
}