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