2015-10-10 16:41:19 +02:00
|
|
|
// Copyright (c) 2015- PPSSPP Project.
|
|
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Initializing a Vulkan context is quite a complex task!
|
|
|
|
|
// That's not really a strange thing though - you really do have control over everything,
|
|
|
|
|
// and everything needs to be specified. There are no nebulous defaults.
|
|
|
|
|
|
|
|
|
|
// We create a swapchain, and two framebuffers that we can point to two of the images
|
|
|
|
|
// we got from the swap chain. These will be used as backbuffers.
|
|
|
|
|
//
|
|
|
|
|
// We also create a depth buffer. The swap chain will not allocate one for us so we need
|
|
|
|
|
// to manage the memory for it ourselves.
|
|
|
|
|
// The depth buffer will not really be used unless we do "non-buffered" rendering, which will happen
|
|
|
|
|
// directly to one of the backbuffers.
|
|
|
|
|
//
|
|
|
|
|
// Render pass usage
|
|
|
|
|
//
|
|
|
|
|
// In normal buffered rendering mode, we do not begin the "UI" render pass until after we have rendered
|
|
|
|
|
// a frame of PSP graphics. The render pass that we will use then will be the simple "uiPass" that does not
|
|
|
|
|
// bother attaching the depth buffer, and discards all input (no need to even bother clearing as we will
|
|
|
|
|
// draw over the whole backbuffer anyway).
|
|
|
|
|
//
|
|
|
|
|
// However, in non-buffered, we will have to use the depth buffer, and we must begin the rendering pass
|
|
|
|
|
// before we start rendering PSP graphics, and end it only after we have completed rendering the UI on top.
|
|
|
|
|
// We will also use clearing.
|
|
|
|
|
//
|
|
|
|
|
// So it all turns into a single rendering pass, which might be good for performance on some GPUs, but it
|
|
|
|
|
// will complicate things a little.
|
|
|
|
|
//
|
|
|
|
|
// In a first iteration, we will not distinguish between these two cases - we will always create a depth buffer
|
|
|
|
|
// and use the same render pass configuration (clear to black). However, we can later change this so we switch
|
|
|
|
|
// to a non-clearing render pass in buffered mode, which might be a tiny bit faster.
|
|
|
|
|
|
2017-12-18 12:22:12 +01:00
|
|
|
#include <cassert>
|
2015-10-10 16:41:19 +02:00
|
|
|
#include <crtdbg.h>
|
2015-12-31 00:30:52 +01:00
|
|
|
#include <sstream>
|
2015-10-10 16:41:19 +02:00
|
|
|
|
2016-10-12 20:58:50 +02:00
|
|
|
#include "Core/Config.h"
|
2018-06-16 18:42:31 -07:00
|
|
|
#include "Core/ConfigValues.h"
|
2017-12-26 15:59:02 -08:00
|
|
|
#include "Core/System.h"
|
2016-02-21 18:05:01 +01:00
|
|
|
#include "Common/Vulkan/VulkanLoader.h"
|
2016-02-21 20:21:24 +01:00
|
|
|
#include "Common/Vulkan/VulkanContext.h"
|
2015-10-10 16:41:19 +02:00
|
|
|
|
2016-04-07 23:45:23 +02:00
|
|
|
#include "base/stringutil.h"
|
2015-10-10 16:41:19 +02:00
|
|
|
#include "thin3d/thin3d.h"
|
2018-02-25 10:27:59 +01:00
|
|
|
#include "thin3d/thin3d_create.h"
|
2017-11-12 21:50:54 -08:00
|
|
|
#include "thin3d/VulkanRenderManager.h"
|
2016-03-12 14:03:26 -08:00
|
|
|
#include "util/text/parsers.h"
|
2015-10-10 16:41:19 +02:00
|
|
|
#include "Windows/GPU/WindowsVulkanContext.h"
|
|
|
|
|
|
2016-03-14 22:07:37 +01:00
|
|
|
#ifdef _DEBUG
|
2015-12-31 14:06:18 +01:00
|
|
|
static const bool g_validate_ = true;
|
2016-03-14 22:07:37 +01:00
|
|
|
#else
|
|
|
|
|
static const bool g_validate_ = false;
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-02-29 22:45:18 -08:00
|
|
|
static uint32_t FlagsFromConfig() {
|
|
|
|
|
uint32_t flags = 0;
|
|
|
|
|
flags = g_Config.bVSync ? VULKAN_FLAG_PRESENT_FIFO : VULKAN_FLAG_PRESENT_MAILBOX;
|
|
|
|
|
if (g_validate_) {
|
|
|
|
|
flags |= VULKAN_FLAG_VALIDATE;
|
|
|
|
|
}
|
|
|
|
|
return flags;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-10 16:41:19 +02:00
|
|
|
bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) {
|
|
|
|
|
*error_message = "N/A";
|
|
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
if (vulkan_) {
|
2015-10-10 16:41:19 +02:00
|
|
|
*error_message = "Already initialized";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-31 01:07:06 +01:00
|
|
|
init_glslang();
|
|
|
|
|
|
2015-12-31 14:06:18 +01:00
|
|
|
g_LogOptions.breakOnError = true;
|
|
|
|
|
g_LogOptions.breakOnWarning = true;
|
|
|
|
|
g_LogOptions.msgBoxOnError = false;
|
|
|
|
|
|
2016-03-12 14:03:26 -08:00
|
|
|
Version gitVer(PPSSPP_GIT_VERSION);
|
2019-09-01 22:20:44 +02:00
|
|
|
|
|
|
|
|
if (!VulkanLoad()) {
|
|
|
|
|
*error_message = "Failed to load Vulkan driver library";
|
2017-11-16 21:54:48 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-09-01 22:20:44 +02:00
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
vulkan_ = new VulkanContext();
|
2019-09-01 22:20:44 +02:00
|
|
|
|
2017-12-18 12:22:12 +01:00
|
|
|
VulkanContext::CreateInfo info{};
|
|
|
|
|
info.app_name = "PPSSPP";
|
|
|
|
|
info.app_ver = gitVer.ToInteger();
|
2020-02-29 22:45:18 -08:00
|
|
|
info.flags = FlagsFromConfig();
|
2020-07-18 22:50:26 +02:00
|
|
|
if (VK_SUCCESS != vulkan_->CreateInstance(info)) {
|
|
|
|
|
*error_message = vulkan_->InitError();
|
|
|
|
|
delete vulkan_;
|
|
|
|
|
vulkan_ = nullptr;
|
2017-08-28 14:12:56 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-18 22:50:26 +02:00
|
|
|
int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice);
|
2018-04-15 09:56:37 +02:00
|
|
|
if (deviceNum < 0) {
|
2020-07-18 22:50:26 +02:00
|
|
|
deviceNum = vulkan_->GetBestPhysicalDevice();
|
2018-09-30 00:53:21 -07:00
|
|
|
if (!g_Config.sVulkanDevice.empty())
|
2020-07-18 22:50:26 +02:00
|
|
|
g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName;
|
2018-04-15 09:56:37 +02:00
|
|
|
}
|
2019-10-24 01:29:24 +02:00
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
vulkan_->ChooseDevice(deviceNum);
|
|
|
|
|
if (vulkan_->CreateDevice() != VK_SUCCESS) {
|
|
|
|
|
*error_message = vulkan_->InitError();
|
|
|
|
|
delete vulkan_;
|
|
|
|
|
vulkan_ = nullptr;
|
2016-03-13 09:33:39 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-06-21 22:59:55 +02:00
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
vulkan_->InitSurface(WINDOWSYSTEM_WIN32, (void *)hInst, (void *)hWnd);
|
|
|
|
|
if (!vulkan_->InitSwapchain()) {
|
|
|
|
|
*error_message = vulkan_->InitError();
|
2017-04-15 16:26:26 -07:00
|
|
|
Shutdown();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-10-10 16:41:19 +02:00
|
|
|
|
2017-11-22 10:46:23 +01:00
|
|
|
bool splitSubmit = g_Config.bGfxDebugSplitSubmit;
|
|
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
draw_ = Draw::T3DCreateVulkanContext(vulkan_, splitSubmit);
|
|
|
|
|
SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName);
|
2017-10-20 14:45:00 +02:00
|
|
|
bool success = draw_->CreatePresets();
|
2020-07-19 17:47:02 +02:00
|
|
|
_assert_msg_(success, "Failed to compile preset shaders");
|
2020-07-18 22:50:26 +02:00
|
|
|
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
2017-11-12 21:50:54 -08:00
|
|
|
|
2020-06-21 22:34:37 +02:00
|
|
|
renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
|
|
|
|
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
|
|
|
|
|
if (!renderManager_->HasBackbuffers()) {
|
2017-11-12 21:50:54 -08:00
|
|
|
Shutdown();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-08-22 12:55:30 +02:00
|
|
|
return true;
|
2015-10-10 16:41:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WindowsVulkanContext::Shutdown() {
|
2017-11-04 20:44:11 -07:00
|
|
|
if (draw_)
|
2020-07-18 22:50:26 +02:00
|
|
|
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
2017-08-22 12:55:30 +02:00
|
|
|
|
2017-02-06 11:20:27 +01:00
|
|
|
delete draw_;
|
|
|
|
|
draw_ = nullptr;
|
|
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
vulkan_->WaitUntilQueueIdle();
|
|
|
|
|
vulkan_->DestroySwapchain();
|
|
|
|
|
vulkan_->DestroySurface();
|
|
|
|
|
vulkan_->DestroyDevice();
|
|
|
|
|
vulkan_->DestroyInstance();
|
2017-11-09 16:02:05 +01:00
|
|
|
|
2020-07-18 22:50:26 +02:00
|
|
|
delete vulkan_;
|
|
|
|
|
vulkan_ = nullptr;
|
2020-06-21 22:34:37 +02:00
|
|
|
renderManager_ = nullptr;
|
2015-12-31 01:07:06 +01:00
|
|
|
|
|
|
|
|
finalize_glslang();
|
2015-10-10 16:41:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WindowsVulkanContext::Resize() {
|
2020-07-18 22:50:26 +02:00
|
|
|
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
|
|
|
|
vulkan_->DestroySwapchain();
|
|
|
|
|
vulkan_->UpdateFlags(FlagsFromConfig());
|
|
|
|
|
vulkan_->InitSwapchain();
|
|
|
|
|
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
|
2015-10-10 16:41:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-21 22:34:37 +02:00
|
|
|
void WindowsVulkanContext::Poll() {
|
|
|
|
|
// Check for existing swapchain to avoid issues during shutdown.
|
2020-07-18 22:50:26 +02:00
|
|
|
if (vulkan_->GetSwapchain() && renderManager_->NeedsSwapchainRecreate()) {
|
2020-06-21 22:34:37 +02:00
|
|
|
Resize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-05 21:18:43 +01:00
|
|
|
void *WindowsVulkanContext::GetAPIContext() {
|
2020-07-18 22:50:26 +02:00
|
|
|
return vulkan_;
|
2016-01-05 21:18:43 +01:00
|
|
|
}
|