Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GPU/WindowsVulkanContext.cpp
5663 views
1
// Copyright (c) 2015- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
19
// Initializing a Vulkan context is quite a complex task!
20
// That's not really a strange thing though - you really do have control over everything,
21
// and everything needs to be specified. There are no nebulous defaults.
22
23
// We create a swapchain, and two framebuffers that we can point to two of the images
24
// we got from the swap chain. These will be used as backbuffers.
25
//
26
// We also create a depth buffer. The swap chain will not allocate one for us so we need
27
// to manage the memory for it ourselves.
28
// The depth buffer will not really be used unless we do "non-buffered" rendering, which will happen
29
// directly to one of the backbuffers.
30
//
31
// Render pass usage
32
//
33
// In normal buffered rendering mode, we do not begin the "UI" render pass until after we have rendered
34
// a frame of PSP graphics. The render pass that we will use then will be the simple "uiPass" that does not
35
// bother attaching the depth buffer, and discards all input (no need to even bother clearing as we will
36
// draw over the whole backbuffer anyway).
37
//
38
// However, in non-buffered, we will have to use the depth buffer, and we must begin the rendering pass
39
// before we start rendering PSP graphics, and end it only after we have completed rendering the UI on top.
40
// We will also use clearing.
41
//
42
// So it all turns into a single rendering pass, which might be good for performance on some GPUs, but it
43
// will complicate things a little.
44
//
45
// In a first iteration, we will not distinguish between these two cases - we will always create a depth buffer
46
// and use the same render pass configuration (clear to black). However, we can later change this so we switch
47
// to a non-clearing render pass in buffered mode, which might be a tiny bit faster.
48
49
#include <crtdbg.h>
50
#include <sstream>
51
52
#include "Core/Config.h"
53
#include "Core/ConfigValues.h"
54
#include "Core/System.h"
55
#include "Core/FrameTiming.h"
56
#include "Common/GPU/Vulkan/VulkanLoader.h"
57
#include "Common/GPU/Vulkan/VulkanContext.h"
58
59
#include "Common/GPU/thin3d.h"
60
#include "Common/GPU/thin3d_create.h"
61
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
62
#include "Common/Data/Text/Parsers.h"
63
#include "GPU/Vulkan/VulkanUtil.h"
64
#include "Windows/GPU/WindowsVulkanContext.h"
65
66
#ifdef _DEBUG
67
static const bool g_validate_ = true;
68
#else
69
static const bool g_validate_ = false;
70
#endif
71
72
bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) {
73
*error_message = "N/A";
74
75
if (vulkan_) {
76
*error_message = "Already initialized";
77
return false;
78
}
79
80
init_glslang();
81
82
g_LogOptions.breakOnError = true;
83
g_LogOptions.breakOnWarning = true;
84
g_LogOptions.msgBoxOnError = false;
85
86
Version gitVer(PPSSPP_GIT_VERSION);
87
88
std::string errorStr;
89
if (!VulkanLoad(&errorStr)) {
90
*error_message = "Failed to load Vulkan driver library: ";
91
(*error_message) += errorStr;
92
return false;
93
}
94
95
vulkan_ = new VulkanContext();
96
97
VulkanContext::CreateInfo info{};
98
InitVulkanCreateInfoFromConfig(&info);
99
if (VK_SUCCESS != vulkan_->CreateInstance(info)) {
100
*error_message = vulkan_->InitError();
101
delete vulkan_;
102
vulkan_ = nullptr;
103
return false;
104
}
105
int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice);
106
if (deviceNum < 0) {
107
deviceNum = vulkan_->GetBestPhysicalDevice();
108
if (!g_Config.sVulkanDevice.empty())
109
g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName;
110
}
111
112
if (vulkan_->CreateDevice(deviceNum) != VK_SUCCESS) {
113
*error_message = vulkan_->InitError();
114
delete vulkan_;
115
vulkan_ = nullptr;
116
return false;
117
}
118
119
vulkan_->InitSurface(WINDOWSYSTEM_WIN32, (void *)hInst, (void *)hWnd);
120
121
bool useMultiThreading = g_Config.bRenderMultiThreading;
122
if (g_Config.iInflightFrames == 1) {
123
useMultiThreading = false;
124
}
125
126
draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading);
127
128
VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_);
129
if (!vulkan_->InitSwapchain(presentMode)) {
130
*error_message = vulkan_->InitError();
131
Shutdown();
132
return false;
133
}
134
135
SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName);
136
bool success = draw_->CreatePresets();
137
_assert_msg_(success, "Failed to compile preset shaders");
138
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
139
140
renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
141
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
142
if (!renderManager_->HasBackbuffers()) {
143
Shutdown();
144
return false;
145
}
146
return true;
147
}
148
149
void WindowsVulkanContext::Shutdown() {
150
if (draw_)
151
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
152
153
delete draw_;
154
draw_ = nullptr;
155
156
vulkan_->WaitUntilQueueIdle();
157
vulkan_->DestroySwapchain();
158
vulkan_->DestroySurface();
159
vulkan_->DestroyDevice();
160
vulkan_->DestroyInstance();
161
162
delete vulkan_;
163
vulkan_ = nullptr;
164
renderManager_ = nullptr;
165
166
finalize_glslang();
167
}
168
169
void WindowsVulkanContext::Resize() {
170
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
171
VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_);
172
vulkan_->InitSwapchain(presentMode);
173
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
174
}
175
176
void WindowsVulkanContext::Poll() {
177
// Check for existing swapchain to avoid issues during shutdown.
178
if (vulkan_->IsSwapchainInited() && renderManager_->NeedsSwapchainRecreate()) {
179
Resize();
180
} else if (vulkan_->IsSwapchainInited() && windowRestored_) {
181
Resize();
182
windowRestored_ = false;
183
}
184
}
185
186
void *WindowsVulkanContext::GetAPIContext() {
187
return vulkan_;
188
}
189
190