Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GPU/WindowsVulkanContext.cpp
3185 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 "Common/GPU/Vulkan/VulkanLoader.h"
56
#include "Common/GPU/Vulkan/VulkanContext.h"
57
58
#include "Common/GPU/thin3d.h"
59
#include "Common/GPU/thin3d_create.h"
60
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
61
#include "Common/Data/Text/Parsers.h"
62
#include "Windows/GPU/WindowsVulkanContext.h"
63
64
#ifdef _DEBUG
65
static const bool g_validate_ = true;
66
#else
67
static const bool g_validate_ = false;
68
#endif
69
70
static VulkanInitFlags FlagsFromConfig() {
71
VulkanInitFlags flags = g_Config.bVSync ? VulkanInitFlags::PRESENT_FIFO : VulkanInitFlags::PRESENT_MAILBOX;
72
if (g_validate_) {
73
flags |= VulkanInitFlags::VALIDATE;
74
}
75
if (g_Config.bVulkanDisableImplicitLayers) {
76
flags |= VulkanInitFlags::DISABLE_IMPLICIT_LAYERS;
77
}
78
return flags;
79
}
80
81
bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_message) {
82
*error_message = "N/A";
83
84
if (vulkan_) {
85
*error_message = "Already initialized";
86
return false;
87
}
88
89
init_glslang();
90
91
g_LogOptions.breakOnError = true;
92
g_LogOptions.breakOnWarning = true;
93
g_LogOptions.msgBoxOnError = false;
94
95
Version gitVer(PPSSPP_GIT_VERSION);
96
97
std::string errorStr;
98
if (!VulkanLoad(&errorStr)) {
99
*error_message = "Failed to load Vulkan driver library: ";
100
(*error_message) += errorStr;
101
return false;
102
}
103
104
vulkan_ = new VulkanContext();
105
106
VulkanContext::CreateInfo info{};
107
info.app_name = "PPSSPP";
108
info.app_ver = gitVer.ToInteger();
109
info.flags = FlagsFromConfig();
110
if (VK_SUCCESS != vulkan_->CreateInstance(info)) {
111
*error_message = vulkan_->InitError();
112
delete vulkan_;
113
vulkan_ = nullptr;
114
return false;
115
}
116
int deviceNum = vulkan_->GetPhysicalDeviceByName(g_Config.sVulkanDevice);
117
if (deviceNum < 0) {
118
deviceNum = vulkan_->GetBestPhysicalDevice();
119
if (!g_Config.sVulkanDevice.empty())
120
g_Config.sVulkanDevice = vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName;
121
}
122
123
if (vulkan_->CreateDevice(deviceNum) != VK_SUCCESS) {
124
*error_message = vulkan_->InitError();
125
delete vulkan_;
126
vulkan_ = nullptr;
127
return false;
128
}
129
130
vulkan_->InitSurface(WINDOWSYSTEM_WIN32, (void *)hInst, (void *)hWnd);
131
if (!vulkan_->InitSwapchain()) {
132
*error_message = vulkan_->InitError();
133
Shutdown();
134
return false;
135
}
136
137
bool useMultiThreading = g_Config.bRenderMultiThreading;
138
if (g_Config.iInflightFrames == 1) {
139
useMultiThreading = false;
140
}
141
142
draw_ = Draw::T3DCreateVulkanContext(vulkan_, useMultiThreading);
143
SetGPUBackend(GPUBackend::VULKAN, vulkan_->GetPhysicalDeviceProperties(deviceNum).properties.deviceName);
144
bool success = draw_->CreatePresets();
145
_assert_msg_(success, "Failed to compile preset shaders");
146
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
147
148
renderManager_ = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
149
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
150
if (!renderManager_->HasBackbuffers()) {
151
Shutdown();
152
return false;
153
}
154
return true;
155
}
156
157
void WindowsVulkanContext::Shutdown() {
158
if (draw_)
159
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
160
161
delete draw_;
162
draw_ = nullptr;
163
164
vulkan_->WaitUntilQueueIdle();
165
vulkan_->DestroySwapchain();
166
vulkan_->DestroySurface();
167
vulkan_->DestroyDevice();
168
vulkan_->DestroyInstance();
169
170
delete vulkan_;
171
vulkan_ = nullptr;
172
renderManager_ = nullptr;
173
174
finalize_glslang();
175
}
176
177
void WindowsVulkanContext::Resize() {
178
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
179
vulkan_->DestroySwapchain();
180
vulkan_->UpdateInitFlags(FlagsFromConfig());
181
vulkan_->InitSwapchain();
182
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
183
}
184
185
void WindowsVulkanContext::Poll() {
186
// Check for existing swapchain to avoid issues during shutdown.
187
if (vulkan_->IsSwapchainInited() && renderManager_->NeedsSwapchainRecreate()) {
188
Resize();
189
} else if (vulkan_->IsSwapchainInited() && windowRestored_) {
190
Resize();
191
windowRestored_ = false;
192
}
193
}
194
195
void *WindowsVulkanContext::GetAPIContext() {
196
return vulkan_;
197
}
198
199