Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/GPU/Vulkan/VulkanContext.h
3188 views
1
#pragma once
2
3
#include <cstdint>
4
#include <cstring>
5
#include <string>
6
#include <string_view>
7
#include <vector>
8
#include <utility>
9
#include <functional>
10
11
#include "Common/Common.h"
12
#include "Common/Log.h"
13
#include "Common/GPU/Vulkan/VulkanLoader.h"
14
#include "Common/GPU/Vulkan/VulkanDebug.h"
15
#include "Common/GPU/Vulkan/VulkanAlloc.h"
16
#include "Common/GPU/Vulkan/VulkanProfiler.h"
17
18
// Enable or disable a simple logging profiler for Vulkan.
19
// Mostly useful for profiling texture uploads currently, but could be useful for
20
// other things as well. We also have a nice integrated render pass profiler in the queue
21
// runner, but this one is more convenient for transient events.
22
23
#define VK_PROFILE_BEGIN(vulkan, cmd, stage, ...) vulkan->GetProfiler()->Begin(cmd, stage, __VA_ARGS__);
24
#define VK_PROFILE_END(vulkan, cmd, stage) vulkan->GetProfiler()->End(cmd, stage);
25
26
enum class VulkanInitFlags : uint32_t {
27
VALIDATE = (1 << 0),
28
PRESENT_MAILBOX = (1 << 1),
29
PRESENT_IMMEDIATE = (1 << 2),
30
PRESENT_FIFO_RELAXED = (1 << 3),
31
PRESENT_FIFO = (1 << 4),
32
DISABLE_IMPLICIT_LAYERS = (1 << 5),
33
};
34
ENUM_CLASS_BITOPS(VulkanInitFlags);
35
36
enum {
37
VULKAN_VENDOR_NVIDIA = 0x000010de,
38
VULKAN_VENDOR_INTEL = 0x00008086, // Haha!
39
VULKAN_VENDOR_AMD = 0x00001002,
40
VULKAN_VENDOR_ARM = 0x000013B5, // Mali
41
VULKAN_VENDOR_QUALCOMM = 0x00005143,
42
VULKAN_VENDOR_IMGTEC = 0x00001010, // PowerVR
43
VULKAN_VENDOR_APPLE = 0x0000106b, // Apple through MoltenVK
44
VULKAN_VENDOR_MESA = 0x00010005, // lavapipe
45
};
46
47
VK_DEFINE_HANDLE(VmaAllocator);
48
VK_DEFINE_HANDLE(VmaAllocation);
49
50
std::string VulkanVendorString(uint32_t vendorId);
51
52
template<class R, class T> inline void ChainStruct(R &root, T *newStruct) {
53
newStruct->pNext = root.pNext;
54
root.pNext = newStruct;
55
}
56
57
// Not all will be usable on all platforms, of course...
58
enum WindowSystem {
59
#ifdef _WIN32
60
WINDOWSYSTEM_WIN32,
61
#endif
62
#ifdef __ANDROID__
63
WINDOWSYSTEM_ANDROID,
64
#endif
65
#ifdef VK_USE_PLATFORM_METAL_EXT
66
WINDOWSYSTEM_METAL_EXT,
67
#endif
68
#ifdef VK_USE_PLATFORM_XLIB_KHR
69
WINDOWSYSTEM_XLIB,
70
#endif
71
#ifdef VK_USE_PLATFORM_XCB_KHR
72
WINDOWSYSTEM_XCB,
73
#endif
74
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
75
WINDOWSYSTEM_WAYLAND,
76
#endif
77
#ifdef VK_USE_PLATFORM_DISPLAY_KHR
78
WINDOWSYSTEM_DISPLAY,
79
#endif
80
};
81
82
struct VulkanPhysicalDeviceInfo {
83
VkFormat preferredDepthStencilFormat;
84
bool canBlitToPreferredDepthStencilFormat;
85
};
86
87
class VulkanProfiler;
88
class VulkanContext;
89
90
// Extremely rough split of capabilities.
91
enum class PerfClass {
92
SLOW,
93
FAST,
94
};
95
96
// This is a bit repetitive...
97
class VulkanDeleteList {
98
struct BufferWithAlloc {
99
VkBuffer buffer;
100
VmaAllocation alloc;
101
};
102
struct ImageWithAlloc {
103
VkImage image;
104
VmaAllocation alloc;
105
};
106
107
struct Callback {
108
explicit Callback(void(*f)(VulkanContext *vulkan, void *userdata), void *u)
109
: func(f), userdata(u) {
110
}
111
112
void (*func)(VulkanContext *vulkan, void *userdata);
113
void *userdata;
114
};
115
116
public:
117
// NOTE: These all take reference handles so they can zero the input value.
118
void QueueDeleteCommandPool(VkCommandPool &pool) { _dbg_assert_(pool != VK_NULL_HANDLE); cmdPools_.push_back(pool); pool = VK_NULL_HANDLE; }
119
void QueueDeleteDescriptorPool(VkDescriptorPool &pool) { _dbg_assert_(pool != VK_NULL_HANDLE); descPools_.push_back(pool); pool = VK_NULL_HANDLE; }
120
void QueueDeleteShaderModule(VkShaderModule &module) { _dbg_assert_(module != VK_NULL_HANDLE); modules_.push_back(module); module = VK_NULL_HANDLE; }
121
void QueueDeleteBuffer(VkBuffer &buffer) { _dbg_assert_(buffer != VK_NULL_HANDLE); buffers_.push_back(buffer); buffer = VK_NULL_HANDLE; }
122
void QueueDeleteBufferView(VkBufferView &bufferView) { _dbg_assert_(bufferView != VK_NULL_HANDLE); bufferViews_.push_back(bufferView); bufferView = VK_NULL_HANDLE; }
123
void QueueDeleteImageView(VkImageView &imageView) { _dbg_assert_(imageView != VK_NULL_HANDLE); imageViews_.push_back(imageView); imageView = VK_NULL_HANDLE; }
124
void QueueDeleteDeviceMemory(VkDeviceMemory &deviceMemory) { _dbg_assert_(deviceMemory != VK_NULL_HANDLE); deviceMemory_.push_back(deviceMemory); deviceMemory = VK_NULL_HANDLE; }
125
void QueueDeleteSampler(VkSampler &sampler) { _dbg_assert_(sampler != VK_NULL_HANDLE); samplers_.push_back(sampler); sampler = VK_NULL_HANDLE; }
126
void QueueDeletePipeline(VkPipeline &pipeline) { _dbg_assert_(pipeline != VK_NULL_HANDLE); pipelines_.push_back(pipeline); pipeline = VK_NULL_HANDLE; }
127
void QueueDeletePipelineCache(VkPipelineCache &pipelineCache) { _dbg_assert_(pipelineCache != VK_NULL_HANDLE); pipelineCaches_.push_back(pipelineCache); pipelineCache = VK_NULL_HANDLE; }
128
void QueueDeleteRenderPass(VkRenderPass &renderPass) { _dbg_assert_(renderPass != VK_NULL_HANDLE); renderPasses_.push_back(renderPass); renderPass = VK_NULL_HANDLE; }
129
void QueueDeleteFramebuffer(VkFramebuffer &framebuffer) { _dbg_assert_(framebuffer != VK_NULL_HANDLE); framebuffers_.push_back(framebuffer); framebuffer = VK_NULL_HANDLE; }
130
void QueueDeletePipelineLayout(VkPipelineLayout &pipelineLayout) { _dbg_assert_(pipelineLayout != VK_NULL_HANDLE); pipelineLayouts_.push_back(pipelineLayout); pipelineLayout = VK_NULL_HANDLE; }
131
void QueueDeleteDescriptorSetLayout(VkDescriptorSetLayout &descSetLayout) { _dbg_assert_(descSetLayout != VK_NULL_HANDLE); descSetLayouts_.push_back(descSetLayout); descSetLayout = VK_NULL_HANDLE; }
132
void QueueDeleteQueryPool(VkQueryPool &queryPool) { _dbg_assert_(queryPool != VK_NULL_HANDLE); queryPools_.push_back(queryPool); queryPool = VK_NULL_HANDLE; }
133
void QueueCallback(void (*func)(VulkanContext *vulkan, void *userdata), void *userdata) { callbacks_.push_back(Callback(func, userdata)); }
134
135
void QueueDeleteBufferAllocation(VkBuffer &buffer, VmaAllocation &alloc) {
136
_dbg_assert_(buffer != VK_NULL_HANDLE);
137
buffersWithAllocs_.push_back(BufferWithAlloc{ buffer, alloc });
138
buffer = VK_NULL_HANDLE;
139
alloc = VK_NULL_HANDLE;
140
}
141
void QueueDeleteImageAllocation(VkImage &image, VmaAllocation &alloc) {
142
_dbg_assert_(image != VK_NULL_HANDLE && alloc != VK_NULL_HANDLE);
143
imagesWithAllocs_.push_back(ImageWithAlloc{ image, alloc });
144
image = VK_NULL_HANDLE;
145
alloc = VK_NULL_HANDLE;
146
}
147
148
void Take(VulkanDeleteList &del);
149
void PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator);
150
151
int GetLastDeleteCount() const {
152
return deleteCount_;
153
}
154
155
private:
156
std::vector<VkCommandPool> cmdPools_;
157
std::vector<VkDescriptorPool> descPools_;
158
std::vector<VkShaderModule> modules_;
159
std::vector<VkBuffer> buffers_;
160
std::vector<BufferWithAlloc> buffersWithAllocs_;
161
std::vector<VkBufferView> bufferViews_;
162
std::vector<ImageWithAlloc> imagesWithAllocs_;
163
std::vector<VkImageView> imageViews_;
164
std::vector<VkDeviceMemory> deviceMemory_;
165
std::vector<VkSampler> samplers_;
166
std::vector<VkPipeline> pipelines_;
167
std::vector<VkPipelineCache> pipelineCaches_;
168
std::vector<VkRenderPass> renderPasses_;
169
std::vector<VkFramebuffer> framebuffers_;
170
std::vector<VkPipelineLayout> pipelineLayouts_;
171
std::vector<VkDescriptorSetLayout> descSetLayouts_;
172
std::vector<VkQueryPool> queryPools_;
173
std::vector<Callback> callbacks_;
174
int deleteCount_ = 0;
175
};
176
177
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
178
class VulkanContext {
179
public:
180
VulkanContext();
181
~VulkanContext();
182
183
struct CreateInfo {
184
const char *app_name;
185
int app_ver;
186
VulkanInitFlags flags;
187
};
188
189
VkResult CreateInstance(const CreateInfo &info);
190
void DestroyInstance();
191
192
int GetBestPhysicalDevice() const;
193
int GetPhysicalDeviceByName(std::string_view name) const;
194
195
// Convenience method to avoid code duplication.
196
// If it returns false, delete the context.
197
bool CreateInstanceAndDevice(const CreateInfo &info);
198
199
// The coreVersion is to avoid enabling extensions that are merged into core Vulkan from a certain version.
200
bool EnableInstanceExtension(const char *extension, uint32_t coreVersion);
201
bool EnableDeviceExtension(const char *extension, uint32_t coreVersion);
202
203
// Was previously two functions, ChooseDevice and CreateDevice.
204
VkResult CreateDevice(int physical_device);
205
206
const std::string &InitError() const { return init_error_; }
207
208
VkDevice GetDevice() const { return device_; }
209
VkInstance GetInstance() const { return instance_; }
210
VulkanInitFlags GetInitFlags() const { return flags_; }
211
void UpdateInitFlags(VulkanInitFlags flags) { flags_ = flags; }
212
213
VulkanDeleteList &Delete() { return globalDeleteList_; }
214
215
// The parameters are whatever the chosen window system wants.
216
// The extents will be automatically determined.
217
VkResult InitSurface(WindowSystem winsys, void *data1, void *data2);
218
VkResult ReinitSurface();
219
220
bool InitSwapchain();
221
void SetCbGetDrawSize(std::function<VkExtent2D()>);
222
223
void DestroySwapchain();
224
void DestroySurface();
225
226
void DestroyDevice();
227
228
void PerformPendingDeletes();
229
void WaitUntilQueueIdle();
230
231
// Utility functions for shorter code
232
VkFence CreateFence(bool presignalled);
233
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag);
234
235
void BeginFrame(VkCommandBuffer firstCommandBuffer);
236
void EndFrame();
237
238
VulkanProfiler *GetProfiler() {
239
return &frame_[curFrame_].profiler;
240
}
241
242
// Simple workaround for the casting warning.
243
template <class T>
244
void SetDebugName(T handle, VkObjectType type, const char *name) {
245
if (extensionsLookup_.EXT_debug_utils && handle != VK_NULL_HANDLE) {
246
_dbg_assert_(handle != VK_NULL_HANDLE);
247
SetDebugNameImpl((uint64_t)handle, type, name);
248
}
249
}
250
bool DebugLayerEnabled() const {
251
return extensionsLookup_.EXT_debug_utils;
252
}
253
254
bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
255
256
VkPhysicalDevice GetPhysicalDevice(int n) const {
257
return physical_devices_[n];
258
}
259
VkPhysicalDevice GetCurrentPhysicalDevice() const {
260
return physical_devices_[physical_device_];
261
}
262
int GetCurrentPhysicalDeviceIndex() const {
263
return physical_device_;
264
}
265
int GetNumPhysicalDevices() const {
266
return (int)physical_devices_.size();
267
}
268
269
VkQueue GetGraphicsQueue() const {
270
return gfx_queue_;
271
}
272
273
int GetGraphicsQueueFamilyIndex() const {
274
return graphics_queue_family_index_;
275
}
276
277
struct PhysicalDeviceProps {
278
VkPhysicalDeviceProperties properties;
279
VkPhysicalDevicePushDescriptorPropertiesKHR pushDescriptorProperties;
280
VkPhysicalDeviceExternalMemoryHostPropertiesEXT externalMemoryHostProperties;
281
VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolve;
282
};
283
284
struct AllPhysicalDeviceFeatures {
285
VkPhysicalDeviceFeatures standard;
286
VkPhysicalDeviceMultiviewFeatures multiview;
287
VkPhysicalDevicePresentWaitFeaturesKHR presentWait;
288
VkPhysicalDevicePresentIdFeaturesKHR presentId;
289
VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertex;
290
};
291
292
const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const {
293
if (i < 0)
294
i = GetCurrentPhysicalDeviceIndex();
295
return physicalDeviceProperties_[i];
296
}
297
298
const VkQueueFamilyProperties &GetQueueFamilyProperties(int family) const {
299
return queueFamilyProperties_[family];
300
}
301
302
VkResult GetInstanceLayerExtensionList(const char *layerName, std::vector<VkExtensionProperties> &extensions);
303
VkResult GetInstanceLayerProperties();
304
305
VkResult GetDeviceLayerExtensionList(const char *layerName, std::vector<VkExtensionProperties> &extensions);
306
VkResult GetDeviceLayerProperties();
307
308
const std::vector<VkExtensionProperties> &GetDeviceExtensionsAvailable() const {
309
return device_extension_properties_;
310
}
311
const std::vector<const char *> &GetDeviceExtensionsEnabled() const {
312
return device_extensions_enabled_;
313
}
314
315
const std::vector<VkExtensionProperties> &GetInstanceExtensionsAvailable() const {
316
return instance_extension_properties_;
317
}
318
const std::vector<const char *> &GetInstanceExtensionsEnabled() const {
319
return instance_extensions_enabled_;
320
}
321
322
const VkPhysicalDeviceMemoryProperties &GetMemoryProperties() const {
323
return memory_properties_;
324
}
325
326
struct PhysicalDeviceFeatures {
327
AllPhysicalDeviceFeatures available{};
328
AllPhysicalDeviceFeatures enabled{};
329
};
330
331
const PhysicalDeviceFeatures &GetDeviceFeatures() const { return deviceFeatures_; }
332
const VulkanPhysicalDeviceInfo &GetDeviceInfo() const { return deviceInfo_; }
333
const VkSurfaceCapabilitiesKHR &GetSurfaceCapabilities() const { return surfCapabilities_; }
334
335
bool IsInstanceExtensionAvailable(const char *extensionName) const {
336
for (const auto &iter : instance_extension_properties_) {
337
if (!strcmp(extensionName, iter.extensionName))
338
return true;
339
}
340
341
// Also search through the layers, one of them might carry the extension (especially DEBUG_utils)
342
for (const auto &iter : instance_layer_properties_) {
343
for (const auto &ext : iter.extensions) {
344
if (!strcmp(extensionName, ext.extensionName)) {
345
return true;
346
}
347
}
348
}
349
350
return false;
351
}
352
353
bool IsDeviceExtensionAvailable(const char *name) const {
354
for (auto &iter : device_extension_properties_) {
355
if (!strcmp(name, iter.extensionName))
356
return true;
357
}
358
return false;
359
}
360
361
int GetInflightFrames() const {
362
// out of MAX_INFLIGHT_FRAMES.
363
return inflightFrames_;
364
}
365
366
// Don't call while a frame is in progress.
367
void UpdateInflightFrames(int n);
368
369
int GetCurFrame() const {
370
return curFrame_;
371
}
372
373
VkSwapchainKHR GetSwapchain() const { return swapchain_; }
374
VkFormat GetSwapchainFormat() const { return swapchainFormat_; }
375
bool IsSwapchainInited() const { return swapchainInited_; }
376
bool HasRealSwapchain() const { return swapChainExtent_.width > 0; }
377
378
int GetBackbufferWidth() { return (int)swapChainExtent_.width; }
379
int GetBackbufferHeight() { return (int)swapChainExtent_.height; }
380
381
void SetProfilerEnabledPtr(bool *enabled) {
382
for (auto &frame : frame_) {
383
frame.profiler.SetEnabledPtr(enabled);
384
}
385
}
386
387
// 1 for no frame overlap and thus minimal latency but worst performance.
388
// 2 is an OK compromise, while 3 performs best but risks slightly higher latency.
389
enum {
390
MAX_INFLIGHT_FRAMES = 3,
391
};
392
393
const VulkanExtensions &Extensions() { return extensionsLookup_; }
394
395
PerfClass DevicePerfClass() const {
396
return devicePerfClass_;
397
}
398
399
void GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation);
400
401
VmaAllocator Allocator() const {
402
return allocator_;
403
}
404
405
const std::vector<VkSurfaceFormatKHR> &SurfaceFormats() {
406
return surfFormats_;
407
}
408
409
VkPresentModeKHR GetPresentMode() const {
410
return presentMode_;
411
}
412
413
std::vector<VkPresentModeKHR> GetAvailablePresentModes() const {
414
return availablePresentModes_;
415
}
416
417
int GetLastDeleteCount() const {
418
return frame_[curFrame_].deleteList.GetLastDeleteCount();
419
}
420
421
u32 InstanceApiVersion() const {
422
return vulkanInstanceApiVersion_;
423
}
424
425
u32 DeviceApiVersion() const {
426
return vulkanDeviceApiVersion_;
427
}
428
429
private:
430
bool ChooseQueue();
431
432
void SetDebugNameImpl(uint64_t handle, VkObjectType type, const char *name);
433
434
VkResult InitDebugUtilsCallback();
435
436
// A layer can expose extensions, keep track of those extensions here.
437
struct LayerProperties {
438
VkLayerProperties properties;
439
std::vector<VkExtensionProperties> extensions;
440
};
441
442
bool CheckLayers(const std::vector<LayerProperties> &layer_props, const std::vector<const char *> &layer_names) const;
443
444
WindowSystem winsys_{};
445
446
// Don't use the real types here to avoid having to include platform-specific stuff
447
// that we really don't want in everything that uses VulkanContext.
448
void *winsysData1_ = nullptr;
449
void *winsysData2_ = nullptr;
450
std::function<VkExtent2D()> cbGetDrawSize_;
451
452
VkInstance instance_ = VK_NULL_HANDLE;
453
VkDevice device_ = VK_NULL_HANDLE;
454
VkQueue gfx_queue_ = VK_NULL_HANDLE;
455
VkSurfaceKHR surface_ = VK_NULL_HANDLE;
456
u32 vulkanInstanceApiVersion_ = 0;
457
u32 vulkanDeviceApiVersion_ = 0;
458
459
std::string init_error_;
460
std::vector<const char *> instance_layer_names_;
461
std::vector<LayerProperties> instance_layer_properties_;
462
463
std::vector<const char *> instance_extensions_enabled_;
464
std::vector<VkExtensionProperties> instance_extension_properties_;
465
466
std::vector<const char *> device_layer_names_;
467
std::vector<LayerProperties> device_layer_properties_;
468
469
std::vector<const char *> device_extensions_enabled_;
470
std::vector<VkExtensionProperties> device_extension_properties_;
471
VulkanExtensions extensionsLookup_{};
472
473
std::vector<VkPhysicalDevice> physical_devices_;
474
475
int physical_device_ = -1;
476
477
uint32_t graphics_queue_family_index_ = -1;
478
std::vector<PhysicalDeviceProps> physicalDeviceProperties_;
479
std::vector<VkQueueFamilyProperties> queueFamilyProperties_;
480
481
VkPhysicalDeviceMemoryProperties memory_properties_{};
482
483
// Custom collection of things that are good to know
484
VulkanPhysicalDeviceInfo deviceInfo_{};
485
486
// Swap chain extent
487
VkExtent2D swapChainExtent_{};
488
489
VulkanInitFlags flags_{};
490
PerfClass devicePerfClass_ = PerfClass::SLOW;
491
492
int inflightFrames_ = MAX_INFLIGHT_FRAMES;
493
494
struct FrameData {
495
FrameData() {}
496
VulkanDeleteList deleteList;
497
VulkanProfiler profiler;
498
};
499
FrameData frame_[MAX_INFLIGHT_FRAMES];
500
int curFrame_ = 0;
501
502
// At the end of the frame, this is copied into the frame's delete list, so it can be processed
503
// the next time the frame comes around again.
504
VulkanDeleteList globalDeleteList_;
505
506
std::vector<VkDebugUtilsMessengerEXT> utils_callbacks;
507
508
VkSwapchainKHR swapchain_ = VK_NULL_HANDLE;
509
VkFormat swapchainFormat_ = VK_FORMAT_UNDEFINED;
510
511
uint32_t queue_count = 0;
512
bool swapchainInited_ = false;
513
514
PhysicalDeviceFeatures deviceFeatures_;
515
516
VkSurfaceCapabilitiesKHR surfCapabilities_{};
517
std::vector<VkSurfaceFormatKHR> surfFormats_{};
518
519
VkPresentModeKHR presentMode_ = VK_PRESENT_MODE_FIFO_KHR;
520
std::vector<VkPresentModeKHR> availablePresentModes_;
521
522
std::vector<VkCommandBuffer> cmdQueue_;
523
524
VmaAllocator allocator_ = VK_NULL_HANDLE;
525
};
526
527
// GLSL compiler
528
void init_glslang();
529
void finalize_glslang();
530
531
enum class GLSLVariant {
532
VULKAN,
533
GL140,
534
GLES300,
535
};
536
537
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant, std::vector<uint32_t> &spirv, std::string *errorMessage);
538
539
const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace);
540
const char *VulkanFormatToString(VkFormat format);
541
const char *VulkanPresentModeToString(VkPresentModeKHR presentMode);
542
const char *VulkanImageLayoutToString(VkImageLayout imageLayout);
543
544
std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props);
545
std::string FormatAPIVersion(u32 version);
546
547
// Simple heuristic.
548
bool IsHashMaliDriverVersion(const VkPhysicalDeviceProperties &props);
549
550
extern VulkanLogOptions g_LogOptions;
551
552