CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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