Project
Loading...
Searching...
No Matches
GPUDisplayBackendVulkan.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
16#include <vulkan/vulkan.hpp>
17VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
18#include "HandMadeMath.h"
19
20#include "GPUCommonDef.h"
22#include "GPUDisplay.h"
23#include "GPULogging.h"
24#include "GPUParam.h"
25
26#include <mutex>
27#include <iostream>
28
29using namespace o2::gpu;
30
31#define GPUCA_DISPLAY_SHADER_TYPE_VULKAN
32#include "GPUDisplayLoadShaders.h"
33
34// #define CHKERR(cmd) {cmd;}
35#define CHKERR(cmd) \
36 do { \
37 auto tmp_internal_retVal = cmd; \
38 if ((int32_t)tmp_internal_retVal < 0) { \
39 GPUError("VULKAN ERROR: %d: %s (%s: %d)", (int32_t)tmp_internal_retVal, "ERROR", __FILE__, __LINE__); \
40 throw std::runtime_error("Vulkan Failure"); \
41 } \
42 } while (false)
43
50
51// ---------------------------- VULKAN HELPERS ----------------------------
52
53static int32_t checkVulkanLayersSupported(const std::vector<const char*>& validationLayers)
54{
55 std::vector<vk::LayerProperties> availableLayers = vk::enumerateInstanceLayerProperties();
56 for (const char* layerName : validationLayers) {
57 bool layerFound = false;
58
59 for (const auto& layerProperties : availableLayers) {
60 if (strcmp(layerName, layerProperties.layerName) == 0) {
61 layerFound = true;
62 break;
63 }
64 }
65
66 if (!layerFound) {
67 return 1;
68 }
69 }
70 return 0;
71}
72
73static uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties, vk::PhysicalDevice physDev)
74{
75 vk::PhysicalDeviceMemoryProperties memProperties = physDev.getMemoryProperties();
76
77 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
78 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
79 return i;
80 }
81 }
82 if ((properties & vk::MemoryPropertyFlagBits::eDeviceLocal) && (properties & vk::MemoryPropertyFlagBits::eHostVisible)) { // No host-visible device memory (e.g. no ReBAR), fall back to host memory
83 return findMemoryType(typeFilter, properties & ~vk::MemoryPropertyFlags(vk::MemoryPropertyFlagBits::eDeviceLocal), physDev);
84 }
85
86 throw std::runtime_error("failed to find suitable memory type!");
87}
88
89static vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats)
90{
91 for (const auto& availableFormat : availableFormats) {
92 if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
93 return availableFormat;
94 }
95 }
96 return availableFormats[0];
97}
98
100{
101 // Lowest latency first. VSync: no tearing, MAILBOX, then FIFO_LATEST_READY (shows the newest ready image at each vblank). No VSync: IMMEDIATE, else the same (often no IMMEDIATE on Wayland). FIFO is always supported.
104 return vk::PresentModeKHR::eFifo; // Refresh rate limit: one frame per refresh, queue depth limited by present wait
105 }
106 const bool vsync = mDisplay->cfgR().drawQualityVSync;
107 const auto& available = mSwapChainDetails.presentModes;
108 for (vk::PresentModeKHR mode : {vk::PresentModeKHR::eImmediate, vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eFifoLatestReady, vk::PresentModeKHR::eFifo}) {
109 if ((mode == vk::PresentModeKHR::eImmediate && vsync) || (mode == vk::PresentModeKHR::eFifoLatestReady && !mFifoLatestReadySupported) || std::find(available.begin(), available.end(), mode) == available.end()) {
110 continue;
111 }
112 if (mode != (vsync ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate) && !mPresentModeErrorShown) {
114 GPUError("VULKAN ERROR: Desired present mode %s not available, using %s", vsync ? "MAILBOX" : "IMMEDIATE", vk::to_string(mode).c_str());
115 }
116 return mode;
117 }
118 return vk::PresentModeKHR::eFifo; // Unreachable, FIFO is always supported
119}
120
121vk::Extent2D GPUDisplayBackendVulkan::chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities)
122{
123 if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
124 return capabilities.currentExtent;
125 } else {
126 int32_t width, height;
128 vk::Extent2D actualExtent = {(uint32_t)width, (uint32_t)height};
129 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
130 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
131 return actualExtent;
132 }
133}
134
135static void cmdImageMemoryBarrier(vk::CommandBuffer cmdbuffer, vk::Image image, vk::PipelineStageFlags2 srcStageMask, vk::AccessFlags2 srcAccessMask, vk::PipelineStageFlags2 dstStageMask, vk::AccessFlags2 dstAccessMask, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor)
136{
137 vk::ImageMemoryBarrier2 barrier{srcStageMask, srcAccessMask, dstStageMask, dstAccessMask, oldLayout, newLayout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, image, {aspect, 0, 1, 0, 1}};
138 cmdbuffer.pipelineBarrier2(vk::DependencyInfo{{}, 0, nullptr, 0, nullptr, 1, &barrier});
139}
140
141static void cmdBeginRendering(vk::CommandBuffer cmdbuffer, uint32_t width, uint32_t height, const vk::RenderingAttachmentInfo& color, const vk::RenderingAttachmentInfo* depth = nullptr)
142{
143 cmdbuffer.beginRendering(vk::RenderingInfo{{}, vk::Rect2D{{0, 0}, {width, height}}, 1, 0, 1, &color, depth});
144 vk::Viewport viewport{0.f, 0.f, (float)width, (float)height, 0.f, 1.f};
145 vk::Rect2D scissor{{0, 0}, {width, height}};
146 cmdbuffer.setViewport(0, 1, &viewport);
147 cmdbuffer.setScissor(0, 1, &scissor);
148}
149
150static void cmdBeginRenderingLoad(vk::CommandBuffer cmdbuffer, vk::Image image, vk::ImageView view, uint32_t width, uint32_t height) // Render on top of the content of a color attachment written by a previous pass
151{
152 cmdImageMemoryBarrier(cmdbuffer, image, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentRead | vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eColorAttachmentOptimal);
153 cmdBeginRendering(cmdbuffer, width, height, vk::RenderingAttachmentInfo{view, vk::ImageLayout::eColorAttachmentOptimal}); // loadOp eLoad, storeOp eStore
154}
155
156void GPUDisplayBackendVulkan::updateSwapChainDetails(const vk::PhysicalDevice& device)
157{
158 mSwapChainDetails.capabilities = device.getSurfaceCapabilitiesKHR(mSurface);
159 mSwapChainDetails.formats = device.getSurfaceFormatsKHR(mSurface);
160 mSwapChainDetails.presentModes = device.getSurfacePresentModesKHR(mSurface);
161}
162
164{
165 vk::CommandBufferAllocateInfo allocInfo{};
166 allocInfo.level = vk::CommandBufferLevel::ePrimary;
167 allocInfo.commandPool = mCommandPool;
168 allocInfo.commandBufferCount = 1;
169 vk::CommandBuffer commandBuffer = mDevice.allocateCommandBuffers(allocInfo)[0];
170 vk::CommandBufferBeginInfo beginInfo{};
171 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
172 commandBuffer.begin(beginInfo);
173 return commandBuffer;
174}
175
176template <class... Args>
177uint64_t GPUDisplayBackendVulkan::queueSubmit(uint64_t* timelineSignalValue, Args&&... args)
178{
179 std::lock_guard<std::mutex> guard(mQueueMutex);
180 if (timelineSignalValue) {
181 *timelineSignalValue = ++mTimelineValue;
182 }
183 CHKERR(mGraphicsQueue.submit2(std::forward<Args>(args)...));
184 return mTimelineValue;
185}
186
188{
189 commandBuffer.end();
190 vk::CommandBufferSubmitInfo commandBufferInfo{commandBuffer};
191 vk::SemaphoreSubmitInfo signalInfo{mTimelineSemaphore, 0, vk::PipelineStageFlagBits2::eAllCommands};
192 vk::SubmitInfo2 submitInfo{{}, 0, nullptr, 1, &commandBufferInfo, 1, &signalInfo};
193 waitTimeline(queueSubmit(&signalInfo.value, 1, &submitInfo, VkFence(VK_NULL_HANDLE)));
194 mDevice.freeCommandBuffers(mCommandPool, 1, &commandBuffer);
195}
196
198{
199 if (value) {
200 vk::SemaphoreWaitInfo waitInfo{{}, 1, &mTimelineSemaphore, &value};
201 CHKERR(mDevice.waitSemaphores(&waitInfo, UINT64_MAX));
202 }
203}
204
205static vk::ImageView createImageViewI(vk::Device device, vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor, uint32_t mipLevels = 1)
206{
207 vk::ImageViewCreateInfo viewInfo{};
208 viewInfo.image = image;
209 viewInfo.viewType = vk::ImageViewType::e2D;
210 viewInfo.format = format;
211 viewInfo.subresourceRange.aspectMask = aspectFlags;
212 viewInfo.subresourceRange.baseMipLevel = 0;
213 viewInfo.subresourceRange.levelCount = mipLevels;
214 viewInfo.subresourceRange.baseArrayLayer = 0;
215 viewInfo.subresourceRange.layerCount = 1;
216 return device.createImageView(viewInfo, nullptr);
217}
218
219static void createImageI(vk::Device device, vk::PhysicalDevice physicalDevice, vk::Image& image, vk::DeviceMemory& imageMemory, uint32_t width, uint32_t height, vk::Format format, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties, vk::ImageTiling tiling = vk::ImageTiling::eOptimal, vk::SampleCountFlagBits numSamples = vk::SampleCountFlagBits::e1, vk::ImageLayout layout = vk::ImageLayout::eUndefined, uint32_t mipLevels = 1)
220{
221 vk::ImageCreateInfo imageInfo{};
222 imageInfo.imageType = vk::ImageType::e2D;
223 imageInfo.extent.width = width;
224 imageInfo.extent.height = height;
225 imageInfo.extent.depth = 1;
226 imageInfo.mipLevels = mipLevels;
227 imageInfo.arrayLayers = 1;
228 imageInfo.format = format;
229 imageInfo.tiling = tiling;
230 imageInfo.initialLayout = layout;
231 imageInfo.usage = usage;
232 imageInfo.samples = numSamples;
233 imageInfo.sharingMode = vk::SharingMode::eExclusive;
234 image = device.createImage(imageInfo);
235
236 vk::MemoryRequirements memRequirements;
237 memRequirements = device.getImageMemoryRequirements(image);
238
239 vk::MemoryAllocateInfo allocInfo{};
240 allocInfo.allocationSize = memRequirements.size;
241 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties, physicalDevice);
242 imageMemory = device.allocateMemory(allocInfo, nullptr);
243
244 device.bindImageMemory(image, imageMemory, 0);
245}
246
247static uint32_t getMaxUsableSampleCount(vk::PhysicalDeviceProperties& physicalDeviceProperties)
248{
249 vk::SampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
250 if (counts & vk::SampleCountFlagBits::e64) {
251 return 64;
252 } else if (counts & vk::SampleCountFlagBits::e32) {
253 return 32;
254 } else if (counts & vk::SampleCountFlagBits::e16) {
255 return 16;
256 } else if (counts & vk::SampleCountFlagBits::e8) {
257 return 8;
258 } else if (counts & vk::SampleCountFlagBits::e4) {
259 return 4;
260 } else if (counts & vk::SampleCountFlagBits::e2) {
261 return 2;
262 }
263 return 1;
264}
265
266static vk::SampleCountFlagBits getMSAASamplesFlag(uint32_t msaa)
267{
268 if (msaa == 2) {
269 return vk::SampleCountFlagBits::e2;
270 } else if (msaa == 4) {
271 return vk::SampleCountFlagBits::e4;
272 } else if (msaa == 8) {
273 return vk::SampleCountFlagBits::e8;
274 } else if (msaa == 16) {
275 return vk::SampleCountFlagBits::e16;
276 } else if (msaa == 32) {
277 return vk::SampleCountFlagBits::e32;
278 } else if (msaa == 64) {
279 return vk::SampleCountFlagBits::e64;
280 }
281 return vk::SampleCountFlagBits::e1;
282}
283
284template <class T, class S>
285static inline void clearVector(T& v, S func, bool downsize = true)
286{
287 std::for_each(v.begin(), v.end(), func);
288 if (downsize) {
289 v.clear();
290 }
291}
292
293// ---------------------------- VULKAN DEVICE MANAGEMENT ----------------------------
294
295template <class T> // Feature structs: sType, pNext, then only VkBool32 members (possibly followed by padding)
296static bool hasFeatures(const T& available, const T& required)
297{
298 const vk::Bool32* a = reinterpret_cast<const vk::Bool32*>(reinterpret_cast<const char*>(&available) + sizeof(VkBaseOutStructure));
299 const vk::Bool32* r = reinterpret_cast<const vk::Bool32*>(reinterpret_cast<const char*>(&required) + sizeof(VkBaseOutStructure));
300 for (size_t i = 0; i < (sizeof(T) - sizeof(VkBaseOutStructure)) / sizeof(vk::Bool32); i++) {
301 if (r[i] && !a[i]) {
302 return false;
303 }
304 }
305 return true;
306}
307
308template <class... T>
309static bool hasFeatures(const vk::StructureChain<T...>& available, const vk::StructureChain<T...>& required)
310{
311 return (hasFeatures(available.template get<T>(), required.template get<T>()) && ...);
312}
313
314template <class... T>
315static void clearFeatures(vk::StructureChain<T...>& chain) // Zero all feature flags including padding, which the member-wise comparison in hasFeatures reads
316{
317 ((memset(reinterpret_cast<char*>(&chain.template get<T>()) + sizeof(VkBaseOutStructure), 0, sizeof(T) - sizeof(VkBaseOutStructure))), ...);
318}
319
320template <class T> // Enable the first available of the extensions (aliases) if the device supports the feature, chaining the feature struct in front of chain
321static bool enableOptionalFeature(vk::PhysicalDevice device, const std::vector<vk::ExtensionProperties>& available, std::initializer_list<const char*> extensions, vk::Bool32 T::* feature, T& featureStruct, std::vector<const char*>& enabledExtensions, void*& chain)
322{
323 for (const char* ext : extensions) {
324 if (std::none_of(available.begin(), available.end(), [ext](const auto& e) { return strcmp(ext, e.extensionName) == 0; })) {
325 continue;
326 }
327 if (!(device.getFeatures2<vk::PhysicalDeviceFeatures2, T>().template get<T>().*feature)) {
328 return false;
329 }
330 enabledExtensions.emplace_back(ext);
331 featureStruct.*feature = VK_TRUE;
332 featureStruct.pNext = chain;
333 chain = &featureStruct;
334 return true;
335 }
336 return false;
337}
338
339template <class... T>
340static vk::StructureChain<T...> getFeatures(vk::PhysicalDevice device, const vk::StructureChain<T...>&)
341{
342 return device.getFeatures2<T...>();
343}
344
345double GPUDisplayBackendVulkan::checkDevice(vk::PhysicalDevice device, const std::vector<const char*>& reqDeviceExtensions, const FeatureChain& reqDeviceFeatures, bool silent)
346{
347 double score = -1.;
348 vk::PhysicalDeviceProperties deviceProperties = device.getProperties();
349 vk::PhysicalDeviceMemoryProperties memoryProperties = device.getMemoryProperties();
350 if (deviceProperties.apiVersion < VK_API_VERSION_1_4) {
351 if (!silent) {
352 GPUInfo("%s ignored, supports only Vulkan %u.%u", &deviceProperties.deviceName[0], VK_API_VERSION_MAJOR(deviceProperties.apiVersion), VK_API_VERSION_MINOR(deviceProperties.apiVersion));
353 }
354 return (-1);
355 }
356
357 std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties(nullptr);
358 uint32_t extensionsFound = 0;
359 for (uint32_t i = 0; i < reqDeviceExtensions.size(); i++) {
360 for (uint32_t j = 0; j < availableExtensions.size(); j++) {
361 if (strcmp(reqDeviceExtensions[i], availableExtensions[j].extensionName) == 0) {
362 extensionsFound++;
363 break;
364 }
365 }
366 }
367 if (extensionsFound < reqDeviceExtensions.size()) {
368 if (!silent) {
369 GPUInfo("%s ignored due to missing extensions", &deviceProperties.deviceName[0]);
370 }
371 return (-1);
372 }
373
374 if (!hasFeatures(getFeatures(device, reqDeviceFeatures), reqDeviceFeatures)) { // Query only after the version and extension checks, the chain contains 1.4 and extension structs
375 if (!silent) {
376 GPUInfo("%s ignored due to missing features", &deviceProperties.deviceName[0]);
377 }
378 return (-1);
379 }
380
381 std::vector<vk::QueueFamilyProperties> queueFamilies = device.getQueueFamilyProperties();
382 bool found = false;
383 for (uint32_t i = 0; i < queueFamilies.size(); i++) {
384 if (!(queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics)) {
385 continue;
386 }
387 vk::Bool32 presentSupport = device.getSurfaceSupportKHR(i, mSurface);
388 if (!presentSupport) {
389 continue;
390 }
392 found = true;
393 break;
394 }
395 if (!found) {
396 if (!silent) {
397 GPUInfo("%s ignored due to missing queue properties", &deviceProperties.deviceName[0]);
398 }
399 return (-1);
400 }
401
404 if (!silent) {
405 GPUInfo("%s ignored due to incompatible swap chain", &deviceProperties.deviceName[0]);
406 }
407 return (-1);
408 }
409
410 score = 1;
411 if (deviceProperties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
412 score += 1e12;
413 } else if (deviceProperties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) {
414 score += 1e11;
415 }
416
417 for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; i++) {
418 if (memoryProperties.memoryHeaps[i].flags & vk::MemoryHeapFlagBits::eDeviceLocal) {
419 score += memoryProperties.memoryHeaps[i].size;
420 }
421 }
422
423 return score;
424}
425
427{
428 VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
429 vk::ApplicationInfo appInfo{};
430 appInfo.pApplicationName = "GPU CA Standalone display";
431 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
432 appInfo.pEngineName = "GPU CI Standalone Engine";
433 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
434 appInfo.apiVersion = VK_API_VERSION_1_4;
435
436 vk::InstanceCreateInfo instanceCreateInfo;
437 instanceCreateInfo.pApplicationInfo = &appInfo;
438
439 const char** frontendExtensions;
440 uint32_t frontendExtensionCount = mDisplay->frontend()->getReqVulkanExtensions(frontendExtensions);
441 std::vector<const char*> reqInstanceExtensions(frontendExtensions, frontendExtensions + frontendExtensionCount);
442 reqInstanceExtensions.insert(reqInstanceExtensions.end(), {VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME}); // The latter two are required by VK_KHR_swapchain_maintenance1
443 std::vector<vk::ExtensionProperties> availableInstanceExtensions = vk::enumerateInstanceExtensionProperties(nullptr);
444 if (mDisplay->GetProcessingSettings().debugLevel >= 3) {
445 std::cout << "available instance extensions: " << availableInstanceExtensions.size() << "\n";
446 for (const auto& extension : availableInstanceExtensions) {
447 std::cout << '\t' << extension.extensionName << '\n';
448 }
449 }
450 for (const char* ext : reqInstanceExtensions) {
451 if (std::none_of(availableInstanceExtensions.begin(), availableInstanceExtensions.end(), [ext](const auto& e) { return strcmp(ext, e.extensionName) == 0; })) {
452 throw std::runtime_error(std::string("Required Vulkan instance extension not available: ") + ext);
453 }
454 }
455
456 const std::vector<const char*> reqValidationLayers = {"VK_LAYER_KHRONOS_validation"};
457 auto debugCallback = [](vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType, const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) -> VkBool32 {
458 static int32_t throwOnError = getenv("GPUCA_VULKAN_VALIDATION_THROW") ? atoi(getenv("GPUCA_VULKAN_VALIDATION_THROW")) : 0;
459 static bool showVulkanValidationInfo = getenv("GPUCA_VULKAN_VALIDATION_INFO") && atoi(getenv("GPUCA_VULKAN_VALIDATION_INFO"));
460 switch (messageSeverity) {
461 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose:
462 if (showVulkanValidationInfo) {
463 GPUInfo("%s", pCallbackData->pMessage);
464 }
465 break;
466 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning:
467 GPUWarning("%s", pCallbackData->pMessage);
468 if (throwOnError > 1) {
469 throw std::logic_error("break_on_validation_warning");
470 }
471 break;
472 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError:
473 GPUError("%s", pCallbackData->pMessage);
474 if (throwOnError) {
475 throw std::logic_error("break_on_validation_error");
476 }
477 break;
478 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo:
479 default:
480 GPUInfo("%s", pCallbackData->pMessage);
481 break;
482 }
483 return false;
484 };
485 vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
487 if (checkVulkanLayersSupported(reqValidationLayers)) {
488 throw std::runtime_error("Requested validation layer support not available");
489 }
490 instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(reqValidationLayers.size());
491 instanceCreateInfo.ppEnabledLayerNames = reqValidationLayers.data();
492 instanceCreateInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
493
494 debugCreateInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
495 debugCreateInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
496 debugCreateInfo.pfnUserCallback = debugCallback;
497 debugCreateInfo.pUserData = nullptr;
498 } else {
499 instanceCreateInfo.enabledLayerCount = 0;
500 }
501
502 instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(reqInstanceExtensions.size());
503 instanceCreateInfo.ppEnabledExtensionNames = reqInstanceExtensions.data();
504
505 mInstance = vk::createInstance(instanceCreateInfo, nullptr);
506 VULKAN_HPP_DEFAULT_DISPATCHER.init(mInstance);
507
509 GPUInfo("Enabling Vulkan Validation Layers");
510 mDebugMessenger = mInstance.createDebugUtilsMessengerEXT(debugCreateInfo, nullptr);
511 }
512
514 throw std::runtime_error("Frontend does not provide Vulkan surface");
515 }
516
517 const std::vector<const char*> reqDeviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, VK_KHR_PRESENT_ID_EXTENSION_NAME, VK_KHR_PRESENT_WAIT_EXTENSION_NAME};
518 FeatureChain reqDeviceFeatures; // Exactly these features are required and enabled
519 clearFeatures(reqDeviceFeatures);
520 auto& reqFeatures10 = reqDeviceFeatures.get<vk::PhysicalDeviceFeatures2>().features;
521 reqFeatures10.geometryShader = reqFeatures10.wideLines = reqFeatures10.largePoints = reqFeatures10.multiDrawIndirect = VK_TRUE;
522 reqDeviceFeatures.get<vk::PhysicalDeviceVulkan12Features>().timelineSemaphore = VK_TRUE;
523 reqDeviceFeatures.get<vk::PhysicalDeviceVulkan14Features>().pushDescriptor = reqDeviceFeatures.get<vk::PhysicalDeviceVulkan14Features>().maintenance5 = VK_TRUE;
524 reqDeviceFeatures.get<vk::PhysicalDeviceVulkan13Features>().dynamicRendering = reqDeviceFeatures.get<vk::PhysicalDeviceVulkan13Features>().synchronization2 = VK_TRUE;
525 reqDeviceFeatures.get<vk::PhysicalDeviceVulkan13Features>().shaderDemoteToHelperInvocation = VK_TRUE; // discard in SPIR-V 1.6
526 reqDeviceFeatures.get<vk::PhysicalDeviceSwapchainMaintenance1FeaturesKHR>().swapchainMaintenance1 = VK_TRUE;
527 reqDeviceFeatures.get<vk::PhysicalDevicePresentIdFeaturesKHR>().presentId = VK_TRUE;
528 reqDeviceFeatures.get<vk::PhysicalDevicePresentWaitFeaturesKHR>().presentWait = VK_TRUE;
529
530 mPhysicalDevice = VkPhysicalDevice(VK_NULL_HANDLE);
531 std::vector<vk::PhysicalDevice> devices = mInstance.enumeratePhysicalDevices();
532 if (devices.size() == 0) {
533 throw std::runtime_error("No Vulkan device present!");
534 }
535 double bestScore = -1.;
536 for (uint32_t i = 0; i < devices.size(); i++) {
537 double score = checkDevice(devices[i], reqDeviceExtensions, reqDeviceFeatures);
538 if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
539 vk::PhysicalDeviceProperties deviceProperties = devices[i].getProperties();
540 GPUInfo("Available Vulkan device %d: %s - Score %f", i, &deviceProperties.deviceName[0], score);
541 }
542 if (score > bestScore && score > 0) {
543 mPhysicalDevice = devices[i];
544 bestScore = score;
545 }
546 }
547 if (mDisplay->cfg().vulkan.forceDevice != -1) {
548 if (mDisplay->cfg().vulkan.forceDevice < 0 || mDisplay->cfg().vulkan.forceDevice >= (int32_t)devices.size()) {
549 throw std::runtime_error("Invalid Vulkan device selected");
550 }
551 mPhysicalDevice = devices[mDisplay->cfg().vulkan.forceDevice];
552 }
553 if (!mPhysicalDevice) {
554 throw std::runtime_error("All available Vulkan devices unsuited");
555 }
556 if (checkDevice(mPhysicalDevice, reqDeviceExtensions, reqDeviceFeatures, true) <= 0) {
557 throw std::runtime_error("Selected vulkan device is incompatible");
558 }
559 mSmoothLinesSupported = getFeatures(mPhysicalDevice, reqDeviceFeatures).get<vk::PhysicalDeviceVulkan14Features>().smoothLines;
560 reqDeviceFeatures.get<vk::PhysicalDeviceVulkan14Features>().smoothLines = mSmoothLinesSupported;
561 std::vector<const char*> deviceExtensions = reqDeviceExtensions;
562 void* featureChain = &reqDeviceFeatures.get<vk::PhysicalDeviceFeatures2>();
563 const std::vector<vk::ExtensionProperties> availableDeviceExtensions = mPhysicalDevice.enumerateDeviceExtensionProperties(nullptr);
564 vk::PhysicalDevicePresentModeFifoLatestReadyFeaturesKHR fifoLatestReadyFeatures{};
565 mFifoLatestReadySupported = enableOptionalFeature(mPhysicalDevice, availableDeviceExtensions, {VK_KHR_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME, VK_EXT_PRESENT_MODE_FIFO_LATEST_READY_EXTENSION_NAME}, &vk::PhysicalDevicePresentModeFifoLatestReadyFeaturesKHR::presentModeFifoLatestReady, fifoLatestReadyFeatures, deviceExtensions, featureChain);
566
568 vk::PhysicalDeviceProperties deviceProperties = mPhysicalDevice.getProperties();
569 vk::FormatProperties depth32FormatProperties = mPhysicalDevice.getFormatProperties(vk::Format::eD32Sfloat);
570 vk::FormatProperties depth64FormatProperties = mPhysicalDevice.getFormatProperties(vk::Format::eD32SfloatS8Uint);
571 GPUInfo("Using physical Vulkan device %s", &deviceProperties.deviceName[0]);
572 mMaxMSAAsupported = getMaxUsableSampleCount(deviceProperties);
573 mStrictLines = deviceProperties.limits.strictLines;
574 mMaxLineWidth = deviceProperties.limits.lineWidthRange[1];
575 mMaxPointSize = deviceProperties.limits.pointSizeRange[1];
576 mMaxRenderSize = std::min({deviceProperties.limits.maxImageDimension2D, deviceProperties.limits.maxFramebufferWidth, deviceProperties.limits.maxFramebufferHeight});
577 mZSupported = (bool)(depth32FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
578 mStencilSupported = (bool)(depth64FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
579 bool mailboxSupported = std::find(mSwapChainDetails.presentModes.begin(), mSwapChainDetails.presentModes.end(), vk::PresentModeKHR::eMailbox) != mSwapChainDetails.presentModes.end();
580 if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
581 GPUInfo("Max MSAA: %d, 32 bit Z buffer %d, 32 bit Z buffer + stencil buffer %d, Mailbox present mode %d, FIFO latest ready present mode %d, smooth lines %d\n", (int32_t)mMaxMSAAsupported, (int32_t)mZSupported, (int32_t)mStencilSupported, (int32_t)mailboxSupported, (int32_t)mFifoLatestReadySupported, (int32_t)mSmoothLinesSupported);
582 }
583
584 vk::DeviceQueueCreateInfo queueCreateInfo{};
585 queueCreateInfo.queueFamilyIndex = mGraphicsFamily;
586 queueCreateInfo.queueCount = 1;
587 float queuePriority = 1.0f;
588 queueCreateInfo.pQueuePriorities = &queuePriority;
589 vk::DeviceCreateInfo deviceCreateInfo{};
590 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
591 deviceCreateInfo.queueCreateInfoCount = 1;
592 deviceCreateInfo.pNext = featureChain;
593 deviceCreateInfo.pEnabledFeatures = nullptr;
594 deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
595 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
596 mDevice = mPhysicalDevice.createDevice(deviceCreateInfo, nullptr);
597 VULKAN_HPP_DEFAULT_DISPATCHER.init(mDevice);
599
600 vk::CommandPoolCreateInfo poolInfo{};
601 poolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
602 poolInfo.queueFamilyIndex = mGraphicsFamily;
603 mCommandPool = mDevice.createCommandPool(poolInfo, nullptr);
604 mPipelineCache = mDevice.createPipelineCache(vk::PipelineCacheCreateInfo{}, nullptr);
605}
606
608{
609 mDevice.destroyCommandPool(mCommandPool, nullptr);
610 mDevice.destroyPipelineCache(mPipelineCache, nullptr);
611 mDevice.destroy(nullptr);
612 mInstance.destroySurfaceKHR(mSurface, nullptr);
614 mInstance.destroyDebugUtilsMessengerEXT(mDebugMessenger, nullptr);
615 }
616}
617
618// ---------------------------- VULKAN COMMAND BUFFERS ----------------------------
619
621{
622 vk::CommandBufferAllocateInfo allocInfo{};
623 allocInfo.commandPool = mCommandPool;
624 allocInfo.level = vk::CommandBufferLevel::ePrimary;
625 allocInfo.commandBufferCount = mImageCount;
626 mCommandBufferUpToDate.assign(mImageCount, false);
627 mCommandBuffers = mDevice.allocateCommandBuffers(allocInfo);
628 mCommandBuffersText = mDevice.allocateCommandBuffers(allocInfo);
629 mCommandBuffersTexture = mDevice.allocateCommandBuffers(allocInfo);
630 mCommandBuffersDownsample = mDevice.allocateCommandBuffers(allocInfo);
631 mCommandBuffersMix = mDevice.allocateCommandBuffers(allocInfo);
632 mCommandBuffersPresent = mDevice.allocateCommandBuffers(allocInfo);
633}
634
636{
637 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffers.size(), mCommandBuffers.data());
638 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersText.size(), mCommandBuffersText.data());
639 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersTexture.size(), mCommandBuffersTexture.data());
641 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersMix.size(), mCommandBuffersMix.data());
642 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersPresent.size(), mCommandBuffersPresent.data());
643}
644
645// ---------------------------- VULKAN SEMAPHORES AND FENCES ----------------------------
646
648{
649 vk::SemaphoreCreateInfo semaphoreInfo{};
652 for (uint32_t i = 0; i < mFramesInFlight; i++) {
653 mImageAvailableSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
654 }
655 for (uint32_t i = 0; i < mImageCount; i++) {
656 mPresentSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
657 }
658 vk::SemaphoreTypeCreateInfo timelineTypeInfo{vk::SemaphoreType::eTimeline, 0};
659 semaphoreInfo.pNext = &timelineTypeInfo;
660 mTimelineSemaphore = mDevice.createSemaphore(semaphoreInfo, nullptr);
661 mTimelineValue = 0;
664 for (uint32_t i = 0; i < mImageCount; i++) {
665 mPresentFences.emplace_back(mDevice.createFence(vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled}, nullptr));
666 }
667}
668
670{
671 if (mPresentFences.size()) { // Timeout in case a failed present did not signal its fence
672 vk::Result retVal = mDevice.waitForFences(mPresentFences.size(), mPresentFences.data(), true, 1'000'000'000);
673 if (retVal != vk::Result::eTimeout) {
674 CHKERR(retVal);
675 }
676 }
677}
678
680{
681 clearVector(mImageAvailableSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
682 clearVector(mPresentSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
683 mDevice.destroySemaphore(mTimelineSemaphore, nullptr);
684 clearVector(mPresentFences, [&](auto& x) { mDevice.destroyFence(x, nullptr); });
685}
686
687// ---------------------------- VULKAN UNIFORM LAYOUTS AND BUFFERS ----------------------------
688
690{
691 for (int32_t j = 0; j < 3; j++) {
693 for (uint32_t i = 0; i < mImageCount; i++) {
694 mUniformBuffersMat[j][i] = createBuffer(sizeof(hmm_mat4), nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0);
695 }
696 }
697
698 vk::DescriptorSetLayoutBinding uboLayoutBindingMat{};
699 uboLayoutBindingMat.binding = 0;
700 uboLayoutBindingMat.descriptorType = vk::DescriptorType::eUniformBuffer;
701 uboLayoutBindingMat.descriptorCount = 1;
702 uboLayoutBindingMat.stageFlags = vk::ShaderStageFlagBits::eVertex;
703 vk::DescriptorSetLayoutBinding samplerLayoutBinding{};
704 samplerLayoutBinding.binding = 2;
705 samplerLayoutBinding.descriptorCount = 1;
706 samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
707 samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
708 vk::DescriptorSetLayoutBinding bindings[2] = {uboLayoutBindingMat, samplerLayoutBinding};
709
710 vk::DescriptorSetLayoutCreateInfo layoutInfo{};
711 layoutInfo.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptor;
712 layoutInfo.bindingCount = 1;
713 layoutInfo.pBindings = bindings;
714 mUniformDescriptor = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
715 layoutInfo.bindingCount = 2;
716 mUniformDescriptorTexture = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
717
719}
720
722{
723 mDevice.destroyDescriptorSetLayout(mUniformDescriptor, nullptr);
724 mDevice.destroyDescriptorSetLayout(mUniformDescriptorTexture, nullptr);
725 for (int32_t j = 0; j < 3; j++) {
726 clearVector(mUniformBuffersMat[j], [&](auto& x) { clearBuffer(x); });
727 }
728}
729
730void GPUDisplayBackendVulkan::cmdPushDescriptors(vk::CommandBuffer commandBuffer, vk::PipelineLayout layout, const VulkanBuffer& ubo, vk::ImageView textureView)
731{
732 vk::DescriptorBufferInfo bufferInfo{ubo.buffer, 0, ubo.size};
733 vk::DescriptorImageInfo imageInfo{mTextureSampler, textureView, vk::ImageLayout::eShaderReadOnlyOptimal};
734 const vk::WriteDescriptorSet writes[2] = {{{}, 0, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &bufferInfo}, {{}, 2, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo}};
735 commandBuffer.pushDescriptorSet(vk::PipelineBindPoint::eGraphics, layout, 0, textureView ? 2 : 1, writes);
736}
737
738// ---------------------------- VULKAN TEXTURE SAMPLER ----------------------------
739
741{
742 vk::SamplerCreateInfo samplerInfo{};
743 samplerInfo.magFilter = vk::Filter::eLinear;
744 samplerInfo.minFilter = vk::Filter::eLinear;
745 samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
746 samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
747 samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
748 samplerInfo.compareEnable = false;
749 samplerInfo.compareOp = vk::CompareOp::eAlways;
750 samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
751 samplerInfo.unnormalizedCoordinates = false;
752 samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
753 samplerInfo.mipLodBias = 0.0f;
754 samplerInfo.minLod = 0.0f;
755 samplerInfo.maxLod = 0.0f;
756 mTextureSampler = mDevice.createSampler(samplerInfo, nullptr);
757}
758
760{
761 mDevice.destroySampler(mTextureSampler, nullptr);
762}
763
764// ---------------------------- VULKAN SWAPCHAIN MANAGEMENT ----------------------------
765
766void GPUDisplayBackendVulkan::createSwapChain(vk::SwapchainKHR oldSwapChain)
767{
769 mSurfaceFormat = chooseSwapSurfaceFormat(mSwapChainDetails.formats);
771 if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
772 GPUInfo("Vulkan present mode %s (VSync %d)", vk::to_string(mPresentMode).c_str(), (int32_t)mDisplay->cfgR().drawQualityVSync);
773 }
774 vk::Extent2D extent = chooseSwapExtent(mSwapChainDetails.capabilities);
775
776 uint32_t imageCount = std::max<uint32_t>(mSwapChainDetails.capabilities.minImageCount + 1, mDisplay->cfg().vulkan.nFramesInFlight + 1);
777 if (mSwapChainDetails.capabilities.maxImageCount > 0 && imageCount > mSwapChainDetails.capabilities.maxImageCount) {
778 imageCount = mSwapChainDetails.capabilities.maxImageCount;
779 }
780
781 mScreenWidth = extent.width;
782 mScreenHeight = extent.height;
783
784 vk::SwapchainCreateInfoKHR swapCreateInfo{};
785 swapCreateInfo.surface = mSurface;
786 swapCreateInfo.minImageCount = imageCount;
787 swapCreateInfo.imageFormat = mSurfaceFormat.format;
788 swapCreateInfo.imageColorSpace = mSurfaceFormat.colorSpace;
789 swapCreateInfo.imageExtent = extent;
790 swapCreateInfo.imageArrayLayers = 1;
791 swapCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst; // Transfer for screenshots / FSAA downsampling, so that they need no swapchain recreation
792 swapCreateInfo.imageSharingMode = vk::SharingMode::eExclusive;
793 swapCreateInfo.queueFamilyIndexCount = 0; // Optional
794 swapCreateInfo.pQueueFamilyIndices = nullptr; // Optional
795 swapCreateInfo.preTransform = mSwapChainDetails.capabilities.currentTransform;
796 swapCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
797 swapCreateInfo.presentMode = mPresentMode;
798 swapCreateInfo.clipped = true;
799 swapCreateInfo.oldSwapchain = oldSwapChain;
800 vk::SwapchainPresentScalingCreateInfoKHR scalingInfo{};
801 { // Let the compositor scale the last image while resizing, until the new swapchain is presented
802 const vk::SurfacePresentModeKHR surfacePresentMode{mPresentMode};
803 const auto caps = mPhysicalDevice.getSurfaceCapabilities2KHR<vk::SurfaceCapabilities2KHR, vk::SurfacePresentScalingCapabilitiesKHR>(vk::PhysicalDeviceSurfaceInfo2KHR{mSurface, &surfacePresentMode}).get<vk::SurfacePresentScalingCapabilitiesKHR>();
804 auto gravity = [](vk::PresentGravityFlagsKHR f) { return (f & vk::PresentGravityFlagBitsKHR::eCentered) ? vk::PresentGravityFlagBitsKHR::eCentered : (f & vk::PresentGravityFlagBitsKHR::eMin) ? vk::PresentGravityFlagBitsKHR::eMin
805 : vk::PresentGravityFlagsKHR(f & vk::PresentGravityFlagBitsKHR::eMax); };
806 scalingInfo.scalingBehavior = (caps.supportedPresentScaling & vk::PresentScalingFlagBitsKHR::eAspectRatioStretch) ? vk::PresentScalingFlagBitsKHR::eAspectRatioStretch : vk::PresentScalingFlagsKHR(caps.supportedPresentScaling & vk::PresentScalingFlagBitsKHR::eStretch);
807 scalingInfo.presentGravityX = gravity(caps.supportedPresentGravityX);
808 scalingInfo.presentGravityY = gravity(caps.supportedPresentGravityY);
809 if (scalingInfo.scalingBehavior && scalingInfo.presentGravityX && scalingInfo.presentGravityY) {
810 swapCreateInfo.pNext = &scalingInfo;
811 }
812 }
813 mSwapChain = mDevice.createSwapchainKHR(swapCreateInfo, nullptr);
814 mLastPresentId = 0; // Present IDs are per swapchain
815 if (oldSwapChain) {
816 waitPresentFences(); // Presentation engine done with the old swapchain
817 mDevice.destroySwapchainKHR(oldSwapChain, nullptr);
818 }
819
820 mSwapChainImages = mDevice.getSwapchainImagesKHR(mSwapChain);
821 uint32_t oldFramesInFlight = mFramesInFlight, oldImageCount = mImageCount;
822 mFramesInFlight = mDisplay->cfg().vulkan.nFramesInFlight;
824 if (mImageCount > sizeof(mUpdateUBOdrawMatrix) * 8) {
825 throw std::runtime_error("Too many swapchain images");
826 }
827
828 if (mFramesInFlight != oldFramesInFlight || mImageCount != oldImageCount || !mCommandInfrastructureCreated) {
830 clearUniformLayoutsAndBuffers(); // TODO: Check what must actually be cleaned up if frames in flight changes
833 }
838 }
839
841 for (uint32_t i = 0; i < mImageCount; i++) {
842 mSwapChainImageViews[i] = createImageViewI(mDevice, mSwapChainImages[i], mSurfaceFormat.format);
843 mCommandBuffersPresent[i].reset({});
844 mCommandBuffersPresent[i].begin(vk::CommandBufferBeginInfo{});
845 cmdImageMemoryBarrier(mCommandBuffersPresent[i], mSwapChainImages[i], vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eNone, vk::AccessFlagBits2::eNone, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::ePresentSrcKHR);
847 }
848}
849
851{
852 clearVector(mSwapChainImageViews, [&](auto& x) { mDevice.destroyImageView(x, nullptr); });
853 if (!keepSwapChain) {
855 mDevice.destroySwapchainKHR(mSwapChain, nullptr);
856 }
857}
858
859void GPUDisplayBackendVulkan::recreateRendering(bool forScreenshot, bool forMixing)
860{
862 const bool needUpdateSwapChain = mMustUpdateSwapChain;
863 const bool needUpdateOffscreenBuffers = needUpdateSwapChain || mDownsampleFactor != getDownsampleFactor(forScreenshot) || mMSAASampleCount != getMSAASamplesFlag(std::min<uint32_t>(mMaxMSAAsupported, mDisplay->cfgR().drawQualityMSAA)) || mZActive != (mZSupported && mDisplay->cfgL().depthBuffer) || mMixingSupported != forMixing;
864 const bool smoothLinesChanged = mSmoothLines != useNativeSmoothLines();
865 if (!needUpdateOffscreenBuffers && !smoothLinesChanged) {
866 return;
867 }
868 mDevice.waitIdle();
869 const vk::Format oldFormat = mSurfaceFormat.format;
870 const vk::SampleCountFlagBits oldMSAASampleCount = mMSAASampleCount;
871 const bool oldZActive = mZActive;
872 if (needUpdateOffscreenBuffers) {
874 if (needUpdateSwapChain) {
875 clearSwapChain(true);
877 }
878 createOffscreenBuffers(forScreenshot, forMixing);
879 mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1; // Screen / render size may have changed
880 }
881 if (mSurfaceFormat.format != oldFormat || mMSAASampleCount != oldMSAASampleCount || mZActive != oldZActive || smoothLinesChanged) { // Viewport / scissor are dynamic
884 }
885}
886
887// ---------------------------- VULKAN OFFSCREEN BUFFERS ----------------------------
888
889void GPUDisplayBackendVulkan::createOffscreenBuffers(bool forScreenshot, bool forMixing)
890{
891 mMSAASampleCount = getMSAASamplesFlag(std::min<uint32_t>(mMaxMSAAsupported, mDisplay->cfgR().drawQualityMSAA));
892 mZActive = mZSupported && mDisplay->cfgL().depthBuffer;
893 mMixingSupported = forMixing;
894 mDownsampleFactor = getDownsampleFactor(forScreenshot);
898
899 const uint32_t imageCountWithMixImages = mImageCount * (mMixingSupported ? 2 : 1);
900 mRenderTargetView.resize(imageCountWithMixImages);
901 mRenderTargetImage.resize(imageCountWithMixImages);
902 if (mDownsampleFSAA) {
903 mDownsampleImages.resize(imageCountWithMixImages);
904 }
905 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) {
906 mMSAAImages.resize(imageCountWithMixImages);
907 }
908 if (mZActive) {
909 mZImages.resize(imageCountWithMixImages);
910 }
912 mMixImages.resize(mImageCount);
913 }
914
915 for (uint32_t i = 0; i < imageCountWithMixImages; i++) {
916 if (i < mImageCount) { // Main render chain
917 // primary buffer mSwapChainImageViews[i] created as part of createSwapChain, not here
918 } else if (!mDownsampleFSAA) { // for rendering to mixBuffer
919 createImageI(mDevice, mPhysicalDevice, mMixImages[i - mImageCount].image, mMixImages[i - mImageCount].memory, mRenderWidth, mRenderHeight, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
920 mMixImages[i - mImageCount].view = createImageViewI(mDevice, mMixImages[i - mImageCount].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
921 }
922 if (mDownsampleFSAA) {
923 vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment;
924 if (i >= mImageCount || mDownsampleFactor != 2) {
925 usage |= vk::ImageUsageFlagBits::eSampled; // Mixing, shader downsampling
926 }
927 if (i < mImageCount && (mDownsampleFactor == 2 || forScreenshot)) {
928 usage |= vk::ImageUsageFlagBits::eTransferSrc; // Blit downsampling, scaled screenshots (blit or copy for screenshot factor <= 2)
929 }
930 createImageI(mDevice, mPhysicalDevice, mDownsampleImages[i].image, mDownsampleImages[i].memory, mRenderWidth, mRenderHeight, mSurfaceFormat.format, usage, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
931 mDownsampleImages[i].view = createImageViewI(mDevice, mDownsampleImages[i].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
934 } else {
937 }
938 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) {
939 createImageI(mDevice, mPhysicalDevice, mMSAAImages[i].image, mMSAAImages[i].memory, mRenderWidth, mRenderHeight, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal, mMSAASampleCount);
940 mMSAAImages[i].view = createImageViewI(mDevice, mMSAAImages[i].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
941 }
942 if (mZActive) {
943 createImageI(mDevice, mPhysicalDevice, mZImages[i].image, mZImages[i].memory, mRenderWidth, mRenderHeight, vk::Format::eD32Sfloat, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal, mMSAASampleCount);
944 mZImages[i].view = createImageViewI(mDevice, mZImages[i].image, vk::Format::eD32Sfloat, vk::ImageAspectFlagBits::eDepth, 1);
945 }
946 }
947
948 if (mMixingSupported) {
949 float vertices[6][4] = {
950 {0, (float)mRenderHeight, 0.0f, 1.0f},
951 {0, 0, 0.0f, 0.0f},
952 {(float)mRenderWidth, 0, 1.0f, 0.0f},
953 {0, (float)mRenderHeight, 0.0f, 1.0f},
954 {(float)mRenderWidth, 0, 1.0f, 0.0f},
955 {(float)mRenderWidth, (float)mRenderHeight, 1.0f, 1.0f}};
956 mMixingTextureVertexArray = createBuffer(sizeof(vertices), &vertices[0][0], vk::BufferUsageFlagBits::eVertexBuffer, 1);
957 }
958
960}
961
963{
964 clearVector(mMSAAImages, [&](auto& x) { clearImage(x); });
965 clearVector(mDownsampleImages, [&](auto& x) { clearImage(x); });
966 clearVector(mZImages, [&](auto& x) { clearImage(x); });
967 clearVector(mMixImages, [&](auto& x) { clearImage(x); });
968 clearVector(mFontVertexBuffer, [&](auto& x) { if (x.size) { clearBuffer(x); } });
969 if (mMixingSupported) {
971 }
972}
973
974// ---------------------------- VULKAN PIPELINE ----------------------------
975
977{
978 vk::PipelineShaderStageCreateInfo shaderStages[2] = {vk::PipelineShaderStageCreateInfo{}, vk::PipelineShaderStageCreateInfo{}};
979 vk::PipelineShaderStageCreateInfo& vertShaderStageInfo = shaderStages[0];
980 vertShaderStageInfo.stage = vk::ShaderStageFlagBits::eVertex;
981 // vertShaderStageInfo.pNext // below: SPIR-V
982 vertShaderStageInfo.pName = "main";
983 vk::PipelineShaderStageCreateInfo& fragShaderStageInfo = shaderStages[1];
984 fragShaderStageInfo.stage = vk::ShaderStageFlagBits::eFragment;
985 // fragShaderStageInfo.pNext // below: SPIR-V
986 fragShaderStageInfo.pName = "main";
987
988 vk::VertexInputBindingDescription bindingDescription{};
989 bindingDescription.binding = 0;
990 // bindingDescription.stride // below
991 bindingDescription.inputRate = vk::VertexInputRate::eVertex;
992
993 vk::VertexInputAttributeDescription attributeDescriptions{};
994 attributeDescriptions.binding = 0;
995 attributeDescriptions.location = 0;
996 // attributeDescriptions.format // below
997 attributeDescriptions.offset = 0;
998
999 vk::PipelineVertexInputStateCreateInfo vertexInputInfo{};
1000 vertexInputInfo.vertexBindingDescriptionCount = 1;
1001 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1002 vertexInputInfo.vertexAttributeDescriptionCount = 1;
1003 vertexInputInfo.pVertexAttributeDescriptions = &attributeDescriptions;
1004 vk::PipelineInputAssemblyStateCreateInfo inputAssembly{};
1005 // inputAssembly.topology // below
1006 inputAssembly.primitiveRestartEnable = false;
1007
1008 vk::PipelineViewportStateCreateInfo viewportState{}; // Viewport and scissor are dynamic
1009 viewportState.viewportCount = 1;
1010 viewportState.scissorCount = 1;
1011
1012 vk::PipelineRasterizationStateCreateInfo rasterizer{};
1013 rasterizer.depthClampEnable = false;
1014 rasterizer.rasterizerDiscardEnable = false;
1015 rasterizer.polygonMode = vk::PolygonMode::eFill;
1016 rasterizer.lineWidth = mDisplay->cfgL().lineWidth;
1017 rasterizer.cullMode = vk::CullModeFlagBits::eBack;
1018 rasterizer.frontFace = vk::FrontFace::eClockwise;
1019 rasterizer.depthBiasEnable = false;
1020 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1021 rasterizer.depthBiasClamp = 0.0f; // Optional
1022 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1023
1024 vk::PipelineMultisampleStateCreateInfo multisampling{};
1025 multisampling.sampleShadingEnable = false;
1026 // multisampling.rasterizationSamples // below
1027 multisampling.minSampleShading = 1.0f; // Optional
1028 multisampling.pSampleMask = nullptr; // Optional
1029 multisampling.alphaToCoverageEnable = false; // Optional
1030 multisampling.alphaToOneEnable = false; // Optional
1031
1032 vk::PipelineColorBlendAttachmentState colorBlendAttachment{};
1033 colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
1034 // colorBlendAttachment.blendEnable // below
1035 colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
1036 colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
1037 colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
1038 colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
1039 colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
1040 colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero;
1041 colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd;
1042
1043 vk::PipelineColorBlendStateCreateInfo colorBlending{};
1044 colorBlending.logicOpEnable = false;
1045 colorBlending.logicOp = vk::LogicOp::eCopy;
1046 colorBlending.attachmentCount = 1;
1047 colorBlending.pAttachments = &colorBlendAttachment;
1048 colorBlending.blendConstants[0] = 0.0f;
1049 colorBlending.blendConstants[1] = 0.0f;
1050 colorBlending.blendConstants[2] = 0.0f;
1051 colorBlending.blendConstants[3] = 0.0f;
1052
1053 vk::PipelineDepthStencilStateCreateInfo depthStencil{};
1054 depthStencil.depthTestEnable = true;
1055 depthStencil.depthWriteEnable = true;
1056 depthStencil.depthCompareOp = vk::CompareOp::eLess;
1057 depthStencil.depthBoundsTestEnable = false;
1058 depthStencil.stencilTestEnable = false;
1059
1060 vk::DynamicState dynamicStates[] = {vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eLineWidth};
1061 vk::PipelineDynamicStateCreateInfo dynamicState{};
1062 // dynamicState.dynamicStateCount // below: viewport + scissor for text / texture, + line width for the main pipelines
1063 dynamicState.pDynamicStates = dynamicStates;
1064
1065 vk::PushConstantRange pushConstantRanges[2] = {vk::PushConstantRange{}, vk::PushConstantRange{}};
1066 pushConstantRanges[0].stageFlags = vk::ShaderStageFlagBits::eFragment;
1067 pushConstantRanges[0].offset = 0;
1068 pushConstantRanges[0].size = sizeof(float) * 4;
1069 pushConstantRanges[1].stageFlags = vk::ShaderStageFlagBits::eVertex;
1070 pushConstantRanges[1].offset = pushConstantRanges[0].size;
1071 pushConstantRanges[1].size = 4 * sizeof(float); // Point size, line width, viewport
1072 vk::PipelineLayoutCreateInfo pipelineLayoutInfo{};
1073 pipelineLayoutInfo.setLayoutCount = 1;
1074 pipelineLayoutInfo.pSetLayouts = &mUniformDescriptor;
1075 pipelineLayoutInfo.pushConstantRangeCount = 2;
1076 pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges;
1077 mPipelineLayout = mDevice.createPipelineLayout(pipelineLayoutInfo, nullptr);
1078 pipelineLayoutInfo.setLayoutCount = 1;
1079 pipelineLayoutInfo.pSetLayouts = &mUniformDescriptorTexture;
1080 mPipelineLayoutTexture = mDevice.createPipelineLayout(pipelineLayoutInfo, nullptr);
1081
1082 vk::GraphicsPipelineCreateInfo pipelineInfo{};
1083 pipelineInfo.stageCount = 2;
1084 pipelineInfo.pVertexInputState = &vertexInputInfo;
1085 pipelineInfo.pInputAssemblyState = &inputAssembly;
1086 pipelineInfo.pViewportState = &viewportState;
1087 pipelineInfo.pRasterizationState = &rasterizer;
1088 pipelineInfo.pMultisampleState = &multisampling;
1089 // pipelineInfo.pDepthStencilState // below
1090 pipelineInfo.pColorBlendState = &colorBlending;
1091 pipelineInfo.pDynamicState = &dynamicState;
1092 // pipelineInfo.layout // below
1093 vk::PipelineRenderingCreateInfo renderingInfo{0, 1, &mSurfaceFormat.format};
1094 pipelineInfo.pNext = &renderingInfo;
1095 pipelineInfo.pStages = shaderStages;
1096 pipelineInfo.basePipelineHandle = VkPipeline(VK_NULL_HANDLE); // Optional
1097 pipelineInfo.basePipelineIndex = -1; // Optional
1098
1100 vk::PipelineRasterizationLineStateCreateInfo smoothLineState{};
1101 smoothLineState.lineRasterizationMode = vk::LineRasterizationMode::eRectangularSmooth; // Coverage goes to alpha, blending is enabled
1102
1103 mPipelines.resize(9); // 0 points, 1 lines, 2 line strip (drawType index), 3 text, 4 texture, 5-7 smooth (shader-based) points, lines, line strip, 8 FSAA downsampling
1104 static constexpr vk::PrimitiveTopology types[3] = {vk::PrimitiveTopology::ePointList, vk::PrimitiveTopology::eLineList, vk::PrimitiveTopology::eLineStrip};
1105 for (uint32_t i = 0; i < mPipelines.size(); i++) {
1106 if (i == 8) { // FSAA downsampling, full-screen triangle without vertex input (last pipeline, the changed states are not reset)
1107 rasterizer.pNext = nullptr;
1108 rasterizer.cullMode = vk::CullModeFlagBits::eNone;
1109 vertexInputInfo.vertexBindingDescriptionCount = vertexInputInfo.vertexAttributeDescriptionCount = 0;
1110 inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
1111 vertShaderStageInfo.pNext = &mShaders.at("vertexFullscreen");
1112 fragShaderStageInfo.pNext = &mShaders.at("fragmentDownsample");
1113 dynamicState.dynamicStateCount = 2;
1114 pipelineInfo.layout = mPipelineLayoutTexture;
1115 renderingInfo.depthAttachmentFormat = vk::Format::eUndefined;
1116 pipelineInfo.pDepthStencilState = nullptr;
1117 colorBlendAttachment.blendEnable = false;
1118 multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
1119 } else if (i == 4) { // Texture rendering
1120 rasterizer.pNext = nullptr;
1121 bindingDescription.stride = 4 * sizeof(float);
1122 attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat;
1123 inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
1124 vertShaderStageInfo.pNext = &mShaders.at("vertexTexture");
1125 fragShaderStageInfo.pNext = &mShaders.at("fragmentTexture");
1126 dynamicState.dynamicStateCount = 2;
1127 pipelineInfo.layout = mPipelineLayoutTexture;
1128 renderingInfo.depthAttachmentFormat = vk::Format::eUndefined;
1129 pipelineInfo.pDepthStencilState = nullptr;
1130 colorBlendAttachment.blendEnable = true;
1131 multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
1132 } else if (i == 3) { // Text rendering
1133 rasterizer.pNext = nullptr;
1134 bindingDescription.stride = 4 * sizeof(float);
1135 attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat;
1136 inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
1137 vertShaderStageInfo.pNext = &mShaders.at("vertexTexture");
1138 fragShaderStageInfo.pNext = &mShaders.at("fragmentText");
1139 dynamicState.dynamicStateCount = 2;
1140 pipelineInfo.layout = mPipelineLayoutTexture;
1141 renderingInfo.depthAttachmentFormat = vk::Format::eUndefined;
1142 pipelineInfo.pDepthStencilState = nullptr;
1143 colorBlendAttachment.blendEnable = true;
1144 multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
1145 } else { // Point / line / line-strip rendering
1146 bindingDescription.stride = 3 * sizeof(float);
1147 attributeDescriptions.format = vk::Format::eR32G32B32Sfloat;
1148 const uint32_t drawIdx = i >= 5 ? i - 5 : i;
1149 inputAssembly.topology = types[drawIdx];
1150 rasterizer.pNext = mSmoothLines && (i == 1 || i == 2) ? &smoothLineState : nullptr;
1151 vertShaderStageInfo.pNext = &mShaders.at(i < 5 ? (drawIdx == 0 ? "vertexPoint" : "vertex") : (drawIdx == 0 ? "vertexSmoothPoint" : "vertexSmoothLine"));
1152 fragShaderStageInfo.pNext = &mShaders.at(i < 5 ? "fragment" : (drawIdx == 0 ? "fragmentSmoothPoint" : "fragmentSmoothLine"));
1153 pipelineInfo.layout = mPipelineLayout;
1154 renderingInfo.depthAttachmentFormat = mZActive ? vk::Format::eD32Sfloat : vk::Format::eUndefined;
1155 pipelineInfo.pDepthStencilState = mZActive ? &depthStencil : nullptr;
1156 dynamicState.dynamicStateCount = 3;
1157 colorBlendAttachment.blendEnable = true;
1158 multisampling.rasterizationSamples = mMSAASampleCount;
1159 }
1160
1161 CHKERR(mDevice.createGraphicsPipelines(mPipelineCache, 1, &pipelineInfo, nullptr, &mPipelines[i]));
1162 }
1163}
1164
1165void GPUDisplayBackendVulkan::startFillCommandBuffer(vk::CommandBuffer& commandBuffer, uint32_t imageIndex, bool toMixBuffer)
1166{
1167 commandBuffer.reset({});
1168
1169 vk::CommandBufferBeginInfo beginInfo{};
1170 beginInfo.flags = {};
1171 commandBuffer.begin(beginInfo);
1172
1173 const uint32_t idx = toMixBuffer ? imageIndex + mImageCount : imageIndex;
1174 const bool msaa = mMSAASampleCount != vk::SampleCountFlagBits::e1;
1175 const vk::PipelineStageFlags2 prevStages = vk::PipelineStageFlagBits2::eColorAttachmentOutput | vk::PipelineStageFlagBits2::eTransfer | vk::PipelineStageFlagBits2::eFragmentShader; // Previous use of the render target (rendering, blit, mix texture), and the wait stage of the acquire semaphore
1176 cmdImageMemoryBarrier(commandBuffer, mRenderTargetImage[idx], prevStages, vk::AccessFlagBits2::eColorAttachmentWrite | vk::AccessFlagBits2::eTransferWrite, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal);
1177 if (msaa) {
1178 cmdImageMemoryBarrier(commandBuffer, mMSAAImages[idx].image, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal);
1179 }
1180 if (mZActive) {
1181 const vk::PipelineStageFlags2 depthStages = vk::PipelineStageFlagBits2::eEarlyFragmentTests | vk::PipelineStageFlagBits2::eLateFragmentTests;
1182 cmdImageMemoryBarrier(commandBuffer, mZImages[idx].image, depthStages, vk::AccessFlagBits2::eDepthStencilAttachmentWrite, depthStages, vk::AccessFlagBits2::eDepthStencilAttachmentRead | vk::AccessFlagBits2::eDepthStencilAttachmentWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal, vk::ImageAspectFlagBits::eDepth);
1183 }
1184
1185 vk::RenderingAttachmentInfo colorAttachment{msaa ? mMSAAImages[idx].view : *mRenderTargetView[idx], vk::ImageLayout::eColorAttachmentOptimal};
1186 colorAttachment.loadOp = vk::AttachmentLoadOp::eClear;
1187 colorAttachment.storeOp = msaa ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore;
1188 colorAttachment.clearValue.color = mDisplay->cfgL().invertColors ? vk::ClearColorValue{std::array<float, 4>{1.0f, 1.0f, 1.0f, 1.0f}} : vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f}};
1189 if (msaa) {
1190 colorAttachment.resolveMode = vk::ResolveModeFlagBits::eAverage;
1191 colorAttachment.resolveImageView = *mRenderTargetView[idx];
1192 colorAttachment.resolveImageLayout = vk::ImageLayout::eColorAttachmentOptimal;
1193 }
1194 vk::RenderingAttachmentInfo depthAttachment{mZActive ? mZImages[idx].view : vk::ImageView{}, vk::ImageLayout::eDepthStencilAttachmentOptimal};
1195 depthAttachment.loadOp = vk::AttachmentLoadOp::eClear;
1196 depthAttachment.storeOp = vk::AttachmentStoreOp::eDontCare;
1197 depthAttachment.clearValue.depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
1198 cmdBeginRendering(commandBuffer, mRenderWidth, mRenderHeight, colorAttachment, mZActive ? &depthAttachment : nullptr);
1199
1200 vk::DeviceSize offsets[] = {0};
1201 if (mVBO.size) {
1202 commandBuffer.bindVertexBuffers(0, 1, &mVBO.buffer, offsets);
1203 }
1204 cmdPushDescriptors(commandBuffer, mPipelineLayout, mUniformBuffersMat[0][imageIndex]);
1205 const float viewport[2] = {(float)mRenderWidth, (float)mRenderHeight};
1206 commandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, 6 * sizeof(float), sizeof(viewport), viewport);
1207}
1208
1209void GPUDisplayBackendVulkan::endFillCommandBuffer(vk::CommandBuffer& commandBuffer)
1210{
1211 commandBuffer.endRendering();
1212 commandBuffer.end();
1213}
1214
1216{
1217 clearVector(mPipelines, [&](auto& x) { mDevice.destroyPipeline(x, nullptr); });
1218 mDevice.destroyPipelineLayout(mPipelineLayout, nullptr);
1219 mDevice.destroyPipelineLayout(mPipelineLayoutTexture, nullptr);
1220}
1221
1222// ---------------------------- VULKAN SHADERS ----------------------------
1223
1224#define LOAD_SHADER(file, ext) \
1225 mShaders[#file] = vk::ShaderModuleCreateInfo { {}, _binary_vulkan_##file##_##ext##_spv_len, reinterpret_cast<const uint32_t*>(_binary_vulkan_##file##_##ext##_spv_start) }
1226
1228{
1229 LOAD_SHADER(vertex, vert);
1230 LOAD_SHADER(fragment, frag);
1231 LOAD_SHADER(fragmentSmoothPoint, frag);
1232 LOAD_SHADER(vertexSmoothLine, vert);
1233 LOAD_SHADER(vertexSmoothPoint, vert);
1234 LOAD_SHADER(fragmentSmoothLine, frag);
1235 LOAD_SHADER(vertexPoint, vert);
1236 LOAD_SHADER(vertexTexture, vert);
1237 LOAD_SHADER(fragmentTexture, frag);
1238 LOAD_SHADER(fragmentText, frag);
1239 LOAD_SHADER(vertexFullscreen, vert);
1240 LOAD_SHADER(fragmentDownsample, frag);
1241}
1242
1243// ---------------------------- VULKAN BUFFERS ----------------------------
1244
1246{
1247 if (buffer.mapped) {
1248 memcpy(buffer.mapped, srcData, size);
1249 } else {
1250 auto tmp = createBuffer(size, srcData, vk::BufferUsageFlagBits::eTransferSrc, 0);
1251
1252 vk::CommandBuffer commandBuffer = getSingleTimeCommandBuffer();
1253 vk::BufferCopy copyRegion{};
1254 copyRegion.size = size;
1255 commandBuffer.copyBuffer(tmp.buffer, buffer.buffer, 1, &copyRegion);
1256 submitSingleTimeCommandBuffer(commandBuffer);
1257
1258 clearBuffer(tmp);
1259 }
1260}
1261
1262GPUDisplayBackendVulkan::VulkanBuffer GPUDisplayBackendVulkan::createBuffer(size_t size, const void* srcData, vk::BufferUsageFlags type, int32_t deviceMemory)
1263{
1264 vk::MemoryPropertyFlags properties;
1265 if (deviceMemory) {
1266 properties |= vk::MemoryPropertyFlagBits::eDeviceLocal;
1267 }
1268 if (deviceMemory == 0 || deviceMemory == 2) {
1269 properties |= (vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
1270 }
1271 if (deviceMemory == 1) {
1272 type |= vk::BufferUsageFlagBits::eTransferDst;
1273 }
1274
1276 vk::BufferCreateInfo bufferInfo{};
1277 bufferInfo.size = size;
1278 bufferInfo.usage = type;
1279 bufferInfo.sharingMode = vk::SharingMode::eExclusive;
1280 buffer.buffer = mDevice.createBuffer(bufferInfo, nullptr);
1281
1282 vk::MemoryRequirements memRequirements;
1283 memRequirements = mDevice.getBufferMemoryRequirements(buffer.buffer);
1284 vk::MemoryAllocateInfo allocInfo{};
1285 allocInfo.allocationSize = memRequirements.size;
1286 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties, mPhysicalDevice);
1287 buffer.memory = mDevice.allocateMemory(allocInfo, nullptr);
1288
1289 mDevice.bindBufferMemory(buffer.buffer, buffer.memory, 0);
1290
1291 buffer.size = size;
1292 buffer.deviceMemory = deviceMemory;
1293 if (deviceMemory != 1) {
1294 CHKERR(mDevice.mapMemory(buffer.memory, 0, VK_WHOLE_SIZE, {}, &buffer.mapped));
1295 }
1296
1297 if (srcData != nullptr) {
1298 writeToBuffer(buffer, size, srcData);
1299 }
1300
1301 return buffer;
1302}
1303
1305{
1306 mDevice.destroyBuffer(buffer.buffer, nullptr);
1307 mDevice.freeMemory(buffer.memory, nullptr); // Implicitly unmaps
1308}
1309
1321
1322// ---------------------------- VULKAN TEXTURES ----------------------------
1323
1324void GPUDisplayBackendVulkan::writeToImage(VulkanImage& image, const void* srcData, size_t srcSize)
1325{
1326 auto tmp = createBuffer(srcSize, srcData, vk::BufferUsageFlagBits::eTransferSrc, 0);
1327
1328 vk::CommandBuffer commandBuffer = getSingleTimeCommandBuffer();
1329 cmdImageMemoryBarrier(commandBuffer, image.image, vk::PipelineStageFlagBits2::eNone, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
1330 vk::BufferImageCopy region{};
1331 region.bufferOffset = 0;
1332 region.bufferRowLength = 0;
1333 region.bufferImageHeight = 0;
1334 region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1335 region.imageSubresource.mipLevel = 0;
1336 region.imageSubresource.baseArrayLayer = 0;
1337 region.imageSubresource.layerCount = 1;
1338 region.imageOffset = vk::Offset3D{0, 0, 0};
1339 region.imageExtent = vk::Extent3D{image.sizex, image.sizey, 1};
1340 commandBuffer.copyBufferToImage(tmp.buffer, image.image, vk::ImageLayout::eTransferDstOptimal, 1, &region);
1341 cmdImageMemoryBarrier(commandBuffer, image.image, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::PipelineStageFlagBits2::eFragmentShader, vk::AccessFlagBits2::eShaderSampledRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
1342 submitSingleTimeCommandBuffer(commandBuffer);
1343
1344 clearBuffer(tmp);
1345}
1346
1347GPUDisplayBackendVulkan::VulkanImage GPUDisplayBackendVulkan::createImage(uint32_t sizex, uint32_t sizey, const void* srcData, size_t srcSize, vk::Format format)
1348{
1350 createImageI(mDevice, mPhysicalDevice, image.image, image.memory, sizex, sizey, format, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal, vk::SampleCountFlagBits::e1);
1351
1352 image.view = createImageViewI(mDevice, image.image, format);
1353
1354 image.sizex = sizex;
1355 image.sizey = sizey;
1356 image.format = format;
1357
1358 if (srcData) {
1359 writeToImage(image, srcData, srcSize);
1360 }
1361 return image;
1362}
1363
1365{
1366 mDevice.destroyImageView(image.view, nullptr);
1367 mDevice.destroyImage(image.image, nullptr);
1368 mDevice.freeMemory(image.memory, nullptr);
1369}
1370
1371// ---------------------------- VULKAN INIT EXIT ----------------------------
1372
1374{
1375 mEnableValidationLayers = (mDisplay->param() && mDisplay->GetProcessingSettings().debugLevel >= 2) || (getenv("GPUCA_VULKAN_VALIDATION") && atoi(getenv("GPUCA_VULKAN_VALIDATION")));
1376 mFramesInFlight = mDisplay->cfg().vulkan.nFramesInFlight;
1377
1378 createDevice();
1379 loadShaders();
1384
1385 return (0);
1386}
1387
1412
1413// ---------------------------- USER CODE ----------------------------
1414
1416{
1417 if (mScreenWidth == width && mScreenHeight == height) {
1418 return;
1419 }
1421 vk::Extent2D extent = chooseSwapExtent(mSwapChainDetails.capabilities);
1422 if (extent.width != mScreenWidth || extent.height != mScreenHeight) {
1423 mMustUpdateSwapChain = true;
1424 }
1425}
1426
1428{
1429 mDevice.waitIdle();
1431 if (totalVertizes) { // Zero-size buffers are invalid (empty event)
1432 mVBO = createBuffer(totalVertizes * sizeof(mDisplay->vertexBuffer()[0][0]), mDisplay->vertexBuffer()[0].data(), vk::BufferUsageFlagBits::eVertexBuffer, 1);
1433 }
1434 if (mDisplay->cfgR().useGLIndirectDraw) {
1436 if (mCmdBuffer.size()) {
1437 mIndirectCommandBuffer = createBuffer(mCmdBuffer.size() * sizeof(mCmdBuffer[0]), mCmdBuffer.data(), vk::BufferUsageFlagBits::eIndirectBuffer, 1);
1438 }
1439 mCmdBuffer.clear();
1440 }
1442}
1443
1445{
1446 auto first = std::get<0>(v);
1447 auto count = std::get<1>(v);
1448 auto iSector = std::get<2>(v);
1449 if (count == 0) {
1450 return 0;
1451 }
1453 return count;
1454 }
1455
1456 const int32_t pipeline = tt + ((tt == POINTS ? mDisplay->cfgL().smoothPoints : useSmoothLineShader()) ? 5 : 0);
1457 if (mCurrentCommandBufferLastPipeline != pipeline) {
1458 mCurrentCommandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[pipeline]);
1460 }
1461 if (mDisplay->cfgR().useGLIndirectDraw) {
1463 } else {
1464 for (uint32_t k = 0; k < count; k++) {
1466 }
1467 }
1468
1469 return count;
1470}
1471
1472bool GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
1473{
1474 if (mDisplay->updateDrawCommands()) { // Main and mix command buffers stay cached while mixing, only the blend pass with the alpha value is recorded per frame
1476 }
1477
1478 if (includeMixImage > 0.f && mFrameSkipped) {
1479 return false; // The render to the mix buffer of this frame did not get an image
1480 }
1481 if (includeMixImage == 0.f) {
1484 if (mLastPresentId > 1 && fpsLimitMode() == FPS_LIMIT_VSYNC) { // Refresh rate limit (FIFO): at most one frame queued (wait for the second-last present), keeps the rate if a frame misses the deadline, timeout in case the window is hidden
1485 const vk::Result retVal = (vk::Result)VULKAN_HPP_DEFAULT_DISPATCHER.vkWaitForPresentKHR(static_cast<VkDevice>(mDevice), static_cast<VkSwapchainKHR>(mSwapChain), mLastPresentId - 1, 100'000'000);
1487 if (retVal != vk::Result::eTimeout && retVal != vk::Result::eErrorOutOfDateKHR) {
1488 CHKERR(retVal);
1489 }
1490 }
1491 auto getImage = [&]() { // Timeout so that a hidden window cannot block the thread (which may also dispatch the window system events) forever
1492 return mDevice.acquireNextImageKHR(mSwapChain, 100'000'000, mImageAvailableSemaphore[mCurrentFrame], VkFence(VK_NULL_HANDLE), &mCurrentImageIndex);
1493 };
1494
1495 vk::Result retVal = vk::Result::eSuccess;
1496 if (mPresentModeForVSyncLimit != (fpsLimitMode() == FPS_LIMIT_VSYNC)) { // Present mode depends on the FPS limit mode
1497 mMustUpdateSwapChain = true;
1498 }
1499 bool mustUpdateSwapChain = mMustUpdateSwapChain || mDisplay->updateRenderPipeline() || (toMixBuffer && !mMixingSupported) || mDownsampleFactor != getDownsampleFactor(requestScreenshot) || mSmoothLines != useNativeSmoothLines();
1500 if (!mustUpdateSwapChain) {
1501 retVal = getImage();
1502 if (retVal == vk::Result::eErrorOutOfDateKHR) {
1503 mustUpdateSwapChain = mMustUpdateSwapChain = true;
1504 }
1505 }
1506 if (mustUpdateSwapChain) {
1507 recreateRendering(requestScreenshot, toMixBuffer);
1508 retVal = getImage();
1509 mMustUpdateSwapChain = false;
1510 }
1512 mFrameSkipped = retVal == vk::Result::eTimeout || retVal == vk::Result::eNotReady || retVal == vk::Result::eErrorOutOfDateKHR;
1513 if (mFrameSkipped) {
1514 return false;
1515 }
1516 CHKERR(retVal);
1517 waitTimeline(mImageTimelineValue[mCurrentImageIndex]); // Previous frame using this image's resources (command buffers, uniform buffer, ...) finished
1518 if (mPresentFences.size()) { // Previous present of this image done, its present semaphore can be signaled again
1519 CHKERR(mDevice.waitForFences(1, &mPresentFences[mCurrentImageIndex], true, 1'000'000'000)); // Timeout (positive result, not an error) in case a failed present did not signal it
1521 }
1522
1523 mHasDrawnText = false;
1524 mFrameCommandBuffers.clear();
1525 const hmm_mat4 modelViewProj = proj * view;
1526 static_assert(sizeof(modelViewProj) == sizeof(mLastMVP));
1527 if (memcmp(&modelViewProj, mLastMVP, sizeof(mLastMVP))) {
1528 memcpy(mLastMVP, &modelViewProj, sizeof(mLastMVP));
1530 }
1534 }
1535 }
1536
1541 }
1542 return true;
1543}
1544
1545void GPUDisplayBackendVulkan::handleSwapChainResult(vk::Result retVal, uint8_t suboptimalBit)
1546{
1547 if (retVal == vk::Result::eErrorOutOfDateKHR || (retVal == vk::Result::eSuboptimalKHR && !(mSwapChainSuboptimal & suboptimalBit))) {
1548 mMustUpdateSwapChain = true;
1549 if (retVal == vk::Result::eSuboptimalKHR) {
1550 mSwapChainSuboptimal |= suboptimalBit;
1551 }
1552 }
1553 if (retVal == vk::Result::eSuccess && !mMustUpdateSwapChain) {
1554 mSwapChainSuboptimal &= ~suboptimalBit;
1555 }
1556}
1557
1558void GPUDisplayBackendVulkan::finishDraw(bool doScreenshot, bool toMixBuffer, float includeMixImage)
1559{
1562 if (!toMixBuffer) { // The mix pass is recorded before the main pass of the same frame, both are valid only after the latter
1564 }
1565 }
1566}
1567
1568void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, float includeMixImage)
1569{
1571 if (!toMixBuffer) {
1572 if (includeMixImage > 0.f) {
1575 }
1576 if (mDownsampleFSAA) {
1579 }
1580
1581 vk::SemaphoreSubmitInfo waitInfo{mImageAvailableSemaphore[mCurrentFrame], 0, vk::PipelineStageFlagBits2::eColorAttachmentOutput | vk::PipelineStageFlagBits2::eTransfer};
1582 vk::SubmitInfo2 submitInfo{{}, 1, &waitInfo};
1583 if (doScreenshot) {
1584 submitInfo.commandBufferInfoCount = mFrameCommandBuffers.size();
1585 submitInfo.pCommandBufferInfos = mFrameCommandBuffers.data();
1586 queueSubmit(nullptr, 1, &submitInfo, VkFence(VK_NULL_HANDLE));
1587 mDevice.waitIdle();
1588 if (mDisplay->cfgR().screenshotScaleFactor != 1) {
1589 readImageToPixels(mDownsampleImages[mCurrentImageIndex].image, mDownsampleImages[mCurrentImageIndex].view, mDownsampleFactor == 2 ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal, mScreenshotPixels);
1590 } else {
1592 }
1593 mFrameCommandBuffers.clear();
1594 submitInfo.waitSemaphoreInfoCount = 0;
1595 }
1596 if (mHasDrawnText) {
1598 }
1600 submitInfo.commandBufferInfoCount = mFrameCommandBuffers.size();
1601 submitInfo.pCommandBufferInfos = mFrameCommandBuffers.data();
1602 vk::SemaphoreSubmitInfo signalInfos[2] = {{mPresentSemaphore[mCurrentImageIndex], 0, vk::PipelineStageFlagBits2::eAllCommands}, {mTimelineSemaphore, 0, vk::PipelineStageFlagBits2::eAllCommands}};
1603 submitInfo.signalSemaphoreInfoCount = 2;
1604 submitInfo.pSignalSemaphoreInfos = signalInfos;
1605 mFrameTimelineValue[mCurrentFrame] = mImageTimelineValue[mCurrentImageIndex] = queueSubmit(&signalInfos[1].value, 1, &submitInfo, VkFence(VK_NULL_HANDLE));
1606
1607 vk::PresentInfoKHR presentInfo{};
1608 presentInfo.waitSemaphoreCount = 1;
1609 presentInfo.pWaitSemaphores = &mPresentSemaphore[mCurrentImageIndex];
1610 presentInfo.swapchainCount = 1;
1611 presentInfo.pSwapchains = &mSwapChain;
1612 presentInfo.pImageIndices = &mCurrentImageIndex;
1613 presentInfo.pResults = nullptr;
1614 const uint64_t presentId = mLastPresentId + 1;
1615 vk::PresentIdKHR presentIdInfo{1, &presentId};
1616 if (fpsLimitMode() == FPS_LIMIT_VSYNC) { // Present wait only for the refresh rate limit, the fpsRate limit (frontend timer) must not be capped at the refresh rate
1617 presentInfo.pNext = &presentIdInfo;
1618 mLastPresentId = presentId;
1619 }
1620 vk::SwapchainPresentFenceInfoKHR presentFenceInfo{};
1621 if (mPresentFences.size()) {
1622 presentFenceInfo = vk::SwapchainPresentFenceInfoKHR{1, &mPresentFences[mCurrentImageIndex], presentInfo.pNext};
1623 presentInfo.pNext = &presentFenceInfo;
1624 }
1625 vk::Result retVal;
1626 {
1627 std::lock_guard<std::mutex> guard(mQueueMutex);
1628 retVal = mGraphicsQueue.presentKHR(&presentInfo);
1629 }
1631 if (retVal != vk::Result::eErrorOutOfDateKHR) {
1632 CHKERR(retVal);
1633 }
1634 }
1635}
1636
1637void GPUDisplayBackendVulkan::downsampleToFramebuffer(vk::CommandBuffer& commandBuffer)
1638{
1639 commandBuffer.reset({});
1640 vk::CommandBufferBeginInfo beginInfo{};
1641 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1642 commandBuffer.begin(beginInfo);
1643
1644 if (mDownsampleFactor != 2) { // Exact box filter by shader, the linear blit below is only exact for factor 2
1645 cmdImageMemoryBarrier(commandBuffer, mSwapChainImages[mCurrentImageIndex], vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal); // Src stage chains with the acquire semaphore wait
1646 cmdImageMemoryBarrier(commandBuffer, mDownsampleImages[mCurrentImageIndex].image, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eFragmentShader, vk::AccessFlagBits2::eShaderSampledRead, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
1648 commandBuffer.end();
1649 return;
1650 }
1651 cmdImageMemoryBarrier(commandBuffer, mSwapChainImages[mCurrentImageIndex], vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); // Src stage chains with the acquire semaphore wait
1652 cmdImageMemoryBarrier(commandBuffer, mDownsampleImages[mCurrentImageIndex].image, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferRead, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eTransferSrcOptimal);
1653
1654 vk::Offset3D blitSizeSrc;
1655 blitSizeSrc.x = mRenderWidth;
1656 blitSizeSrc.y = mRenderHeight;
1657 blitSizeSrc.z = 1;
1658 vk::Offset3D blitSizeDst;
1659 blitSizeDst.x = mScreenWidth;
1660 blitSizeDst.y = mScreenHeight;
1661 blitSizeDst.z = 1;
1662 vk::ImageBlit imageBlitRegion{};
1663 imageBlitRegion.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1664 imageBlitRegion.srcSubresource.layerCount = 1;
1665 imageBlitRegion.srcOffsets[1] = blitSizeSrc;
1666 imageBlitRegion.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1667 imageBlitRegion.dstSubresource.layerCount = 1;
1668 imageBlitRegion.dstOffsets[1] = blitSizeDst;
1669 commandBuffer.blitImage(mDownsampleImages[mCurrentImageIndex].image, vk::ImageLayout::eTransferSrcOptimal, mSwapChainImages[mCurrentImageIndex], vk::ImageLayout::eTransferDstOptimal, 1, &imageBlitRegion, vk::Filter::eLinear);
1670
1671 cmdImageMemoryBarrier(commandBuffer, mSwapChainImages[mCurrentImageIndex], vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentRead | vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eColorAttachmentOptimal); // Next: text pass and / or present transition
1672
1673 commandBuffer.end();
1674}
1675
1676void GPUDisplayBackendVulkan::cmdDownsample(vk::CommandBuffer commandBuffer, vk::ImageView src, vk::ImageView dst, uint32_t width, uint32_t height, int32_t factor)
1677{
1678 vk::RenderingAttachmentInfo colorAttachment{dst, vk::ImageLayout::eColorAttachmentOptimal};
1679 colorAttachment.loadOp = vk::AttachmentLoadOp::eDontCare; // Fully overwritten
1680 cmdBeginRendering(commandBuffer, width, height, colorAttachment);
1681 commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[8]);
1682 cmdPushDescriptors(commandBuffer, mPipelineLayoutTexture, mUniformBuffersMat[2][mCurrentImageIndex], src); // The UBO is not used by the shader
1683 commandBuffer.pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(factor), &factor);
1684 commandBuffer.draw(3, 1, 0, 0);
1685 commandBuffer.endRendering();
1686}
1687
1689{
1692 hmm_mat4 proj = HMM_Orthographic(0.f, mScreenWidth, 0.f, mScreenHeight, -1, 1);
1693 writeToBuffer(mUniformBuffersMat[1][mCurrentImageIndex], sizeof(proj), &proj);
1694 }
1695 mFontVertexBufferHost.clear();
1696 mTextDrawCommands.clear();
1697}
1698
1700{
1701 if (!mHasDrawnText) {
1702 return;
1703 }
1704
1706
1707 vk::CommandBufferBeginInfo beginInfo{};
1708 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1709 mCommandBuffersText[mCurrentImageIndex].begin(beginInfo);
1710
1712
1714 const size_t fontBufferSize = mFontVertexBufferHost.size() * sizeof(mFontVertexBufferHost[0]);
1715 if (fontBuffer.size < fontBufferSize) { // Persistent per image, only grows
1716 if (fontBuffer.size) {
1717 clearBuffer(fontBuffer);
1718 }
1719 fontBuffer = createBuffer(std::max(fontBufferSize, 2 * fontBuffer.size), nullptr, vk::BufferUsageFlagBits::eVertexBuffer, 0);
1720 }
1721 writeToBuffer(fontBuffer, fontBufferSize, mFontVertexBufferHost.data());
1722
1723 mCommandBuffersText[mCurrentImageIndex].bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[3]);
1725 vk::DeviceSize offsets[] = {0};
1726 mCommandBuffersText[mCurrentImageIndex].bindVertexBuffers(0, 1, &fontBuffer.buffer, offsets);
1727
1728 for (const auto& cmd : mTextDrawCommands) {
1729 mCommandBuffersText[mCurrentImageIndex].pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(cmd.color), cmd.color);
1730 mCommandBuffersText[mCurrentImageIndex].draw(cmd.nVertices, 1, cmd.firstVertex, 0);
1731 }
1732
1733 mFontVertexBufferHost.clear();
1734
1737}
1738
1739void GPUDisplayBackendVulkan::mixImages(vk::CommandBuffer commandBuffer, float mixSlaveImage)
1740{
1743 hmm_mat4 proj = HMM_Orthographic(0.f, mRenderWidth, 0.f, mRenderHeight, -1, 1);
1744 writeToBuffer(mUniformBuffersMat[2][mCurrentImageIndex], sizeof(proj), &proj);
1745 }
1746 commandBuffer.reset({});
1747 vk::CommandBufferBeginInfo beginInfo{};
1748 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1749 commandBuffer.begin(beginInfo);
1750
1752 cmdImageMemoryBarrier(commandBuffer, image, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eFragmentShader, vk::AccessFlagBits2::eShaderSampledRead, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
1754
1755 commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[4]);
1757 vk::DeviceSize offsets[] = {0};
1758 commandBuffer.bindVertexBuffers(0, 1, &mMixingTextureVertexArray.buffer, offsets);
1759
1760 commandBuffer.pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(mixSlaveImage), &mixSlaveImage);
1761 commandBuffer.draw(6, 1, 0, 0);
1762
1763 commandBuffer.endRendering();
1764 commandBuffer.end();
1765}
1766
1768{
1770 return;
1771 }
1772 mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(color), color.data());
1773}
1774
1776{
1778 return;
1779 }
1780 float size = std::min(mDisplay->cfgL().pointSize * mDownsampleFactor * factor + (mDisplay->cfgL().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0.f), mMaxPointSize);
1781 mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, sizeof(std::array<float, 4>), sizeof(size), &size);
1782}
1783
1785{
1786 return mDisplay->cfgL().smoothLines && mSmoothLinesSupported && mDisplay->cfgR().rendererCompatSmoothing;
1787}
1788
1790{
1791 return mDisplay->cfgL().smoothLines && !useNativeSmoothLines();
1792}
1793
1795{
1797 return;
1798 }
1799 float width = mDisplay->cfgL().lineWidth * mDownsampleFactor * factor;
1800 if (useSmoothLineShader()) {
1801 const float expand = mStrictLines ? 1.f : 1.4143f; // Non-strict wide lines may be parallelograms, widen so that the perpendicular width is covered
1802 width = std::min(width + SMOOTH_LINE_EXTRA_WIDTH, mMaxLineWidth / expand); // The shader must see the width that is actually rasterized
1803 mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, 5 * sizeof(float), sizeof(width), &width);
1804 width *= expand;
1805 }
1806 mCurrentCommandBuffer.setLineWidth(std::min(width, mMaxLineWidth));
1807}
1808
1813
1814void GPUDisplayBackendVulkan::addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void* data)
1815{
1816 if (symbol != (int32_t)mFontSymbols.size()) {
1817 throw std::runtime_error("Incorrect symbol ID");
1818 }
1819 mFontSymbols.emplace_back(FontSymbolVulkan{{{sizex, sizey}, {offsetx, offsety}, advance}, nullptr, 0.f, 0.f, 0.f, 0.f});
1820 auto& buffer = mFontSymbols.back().data;
1821 if (sizex && sizey) {
1822 buffer.reset(new char[sizex * sizey]);
1823 memcpy(buffer.get(), data, sizex * sizey);
1824 }
1825}
1826
1828{
1829 int32_t maxSizeX = 0, maxSizeY = 0, maxBigX = 0, maxBigY = 0, maxRowY = 0;
1830 bool smooth = smoothFont();
1831 // Build a mega texture containing all fonts
1832 for (auto& symbol : mFontSymbols) {
1833 maxSizeX = std::max(maxSizeX, symbol.size[0]);
1834 maxSizeY = std::max(maxSizeY, symbol.size[1]);
1835 }
1836 uint32_t nn = ceil(std::sqrt(mFontSymbols.size()));
1837 int32_t sizex = nn * maxSizeX;
1838 int32_t sizey = nn * maxSizeY;
1839 std::unique_ptr<char[]> bigImage{new char[sizex * sizey]};
1840 memset(bigImage.get(), 0, sizex * sizey);
1841 int32_t rowy = 0, colx = 0;
1842 for (uint32_t i = 0; i < mFontSymbols.size(); i++) {
1843 auto& s = mFontSymbols[i];
1844 if (colx + s.size[0] > sizex) {
1845 colx = 0;
1846 rowy += maxRowY;
1847 maxRowY = 0;
1848 }
1849 for (int32_t k = 0; k < s.size[1]; k++) {
1850 for (int32_t j = 0; j < s.size[0]; j++) {
1851 int8_t val = s.data.get()[j + k * s.size[0]];
1852 if (!smooth) {
1853 val = val < 0 ? 0xFF : 0;
1854 }
1855 bigImage.get()[(colx + j) + (rowy + k) * sizex] = val;
1856 }
1857 }
1858 s.data.reset();
1859 s.x0 = colx;
1860 s.x1 = colx + s.size[0];
1861 s.y0 = rowy;
1862 s.y1 = rowy + s.size[1];
1863 maxBigX = std::max(maxBigX, colx + s.size[0]);
1864 maxBigY = std::max(maxBigY, rowy + s.size[1]);
1865 maxRowY = std::max(maxRowY, s.size[1]);
1866 colx += s.size[0];
1867 }
1868 if (maxBigX != sizex) {
1869 for (int32_t y = 1; y < maxBigY; y++) {
1870 memmove(bigImage.get() + y * maxBigX, bigImage.get() + y * sizex, maxBigX);
1871 }
1872 }
1873 sizex = maxBigX;
1874 sizey = maxBigY;
1875 for (uint32_t i = 0; i < mFontSymbols.size(); i++) {
1876 auto& s = mFontSymbols[i];
1877 s.x0 /= sizex;
1878 s.x1 /= sizex;
1879 s.y0 /= sizey;
1880 s.y1 /= sizey;
1881 }
1882
1883 mFontImage = createImage(sizex, sizey, bigImage.get(), sizex * sizey, vk::Format::eR8Unorm);
1884}
1885
1887{
1888 mDevice.waitIdle();
1892 }
1893 mFontSymbols.clear();
1894}
1895
1896void GPUDisplayBackendVulkan::OpenGLPrint(const char* s, float x, float y, float* color, float scale)
1897{
1899 return;
1900 }
1901
1902 size_t firstVertex = mFontVertexBufferHost.size() / 4;
1903 if (smoothFont()) {
1904 scale *= 0.25f; // Font size is 48 to have nice bitmap, scale to size 12
1905 }
1906
1907 for (const unsigned char* c = (const unsigned char*)s; *c; c++) {
1908 if ((int32_t)*c >= (int32_t)mFontSymbols.size()) {
1909 GPUError("Trying to draw unsupported symbol: %d > %d\n", (int32_t)*c, (int32_t)mFontSymbols.size());
1910 continue;
1911 }
1912 const FontSymbolVulkan& sym = mFontSymbols[*c];
1913 if (sym.size[0] && sym.size[1]) {
1914 mHasDrawnText = true;
1915 float xpos = x + sym.offset[0] * scale;
1916 float ypos = y - (sym.size[1] - sym.offset[1]) * scale;
1917 float w = sym.size[0] * scale;
1918 float h = sym.size[1] * scale;
1919 float vertices[6][4] = {
1920 {xpos, mScreenHeight - 1 - ypos, sym.x0, sym.y1},
1921 {xpos, mScreenHeight - 1 - (ypos + h), sym.x0, sym.y0},
1922 {xpos + w, mScreenHeight - 1 - ypos, sym.x1, sym.y1},
1923 {xpos + w, mScreenHeight - 1 - ypos, sym.x1, sym.y1},
1924 {xpos, mScreenHeight - 1 - (ypos + h), sym.x0, sym.y0},
1925 {xpos + w, mScreenHeight - 1 - (ypos + h), sym.x1, sym.y0}};
1926 size_t oldSize = mFontVertexBufferHost.size();
1927 mFontVertexBufferHost.resize(oldSize + 4 * 6);
1928 memcpy(&mFontVertexBufferHost[oldSize], &vertices[0][0], sizeof(vertices));
1929 }
1930 x += (sym.advance >> 6) * scale; // shift is in 1/64th of a pixel
1931 }
1932
1933 size_t nVertices = mFontVertexBufferHost.size() / 4 - firstVertex;
1934
1935 if (nVertices) {
1936 auto& c = mTextDrawCommands;
1937 if (c.size() && c.back().color[0] == color[0] && c.back().color[1] == color[1] && c.back().color[2] == color[2] && c.back().color[3] == color[3]) {
1938 c.back().nVertices += nVertices;
1939 } else {
1940 c.emplace_back(TextDrawCommand{firstVertex, nVertices, {color[0], color[1], color[2], color[3]}});
1941 }
1942 }
1943}
1944
1945void GPUDisplayBackendVulkan::readImageToPixels(vk::Image image, vk::ImageView view, vk::ImageLayout layout, std::vector<char>& pixels)
1946{
1947 const vk::Format format = mSurfaceFormat.format;
1948 if (format != vk::Format::eB8G8R8A8Unorm && format != vk::Format::eB8G8R8A8Srgb && format != vk::Format::eR8G8B8A8Unorm && format != vk::Format::eR8G8B8A8Srgb) {
1949 GPUError("Screenshot not supported for swapchain format %s, only 8 bit BGRA / RGBA", vk::to_string(format).c_str());
1950 pixels.clear();
1951 return;
1952 }
1953 uint32_t width = mScreenWidth * mDisplay->cfgR().screenshotScaleFactor;
1954 uint32_t height = mScreenHeight * mDisplay->cfgR().screenshotScaleFactor;
1955 static constexpr int32_t bytesPerPixel = 4;
1956 pixels.resize(width * height * bytesPerPixel);
1957
1958 vk::Image dstImage, src;
1959 vk::ImageView dstView;
1960 vk::DeviceMemory dstImageMemory;
1961 const bool scaled = mDisplay->cfgR().screenshotScaleFactor != 1;
1962 const int32_t factor = mRenderWidth / width; // FSAA factor between the rendered image and the screenshot
1963 const bool shaderDownsample = scaled && factor != 1 && factor != 2; // Exact box filter, the linear blit is only exact for factor 2
1964 const vk::ImageLayout srcLayout = shaderDownsample ? vk::ImageLayout::eShaderReadOnlyOptimal : vk::ImageLayout::eTransferSrcOptimal;
1965 const vk::PipelineStageFlags2 srcStage = shaderDownsample ? vk::PipelineStageFlagBits2::eFragmentShader : vk::PipelineStageFlagBits2::eTransfer;
1966 VulkanBuffer readBuffer = createBuffer(width * height * bytesPerPixel, nullptr, vk::BufferUsageFlagBits::eTransferDst, 0);
1967 vk::CommandBuffer cmdBuffer = getSingleTimeCommandBuffer();
1968 cmdImageMemoryBarrier(cmdBuffer, image, vk::PipelineStageFlagBits2::eColorAttachmentOutput | vk::PipelineStageFlagBits2::eTransfer | vk::PipelineStageFlagBits2::eFragmentShader, vk::AccessFlagBits2::eColorAttachmentWrite | vk::AccessFlagBits2::eTransferWrite, srcStage, shaderDownsample ? vk::AccessFlagBits2::eShaderSampledRead : vk::AccessFlagBits2::eTransferRead, layout, srcLayout);
1969 if (shaderDownsample) {
1970 createImageI(mDevice, mPhysicalDevice, dstImage, dstImageMemory, width, height, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
1971 dstView = createImageViewI(mDevice, dstImage, mSurfaceFormat.format);
1972 cmdImageMemoryBarrier(cmdBuffer, dstImage, vk::PipelineStageFlagBits2::eNone, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal);
1973 cmdDownsample(cmdBuffer, view, dstView, width, height, factor);
1974 cmdImageMemoryBarrier(cmdBuffer, dstImage, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferRead, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eTransferSrcOptimal);
1975 src = dstImage;
1976 } else if (scaled) {
1977 createImageI(mDevice, mPhysicalDevice, dstImage, dstImageMemory, width, height, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
1978 cmdImageMemoryBarrier(cmdBuffer, dstImage, vk::PipelineStageFlagBits2::eNone, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
1979 vk::Offset3D blitSizeSrc = {(int32_t)mRenderWidth, (int32_t)mRenderHeight, 1};
1980 vk::Offset3D blitSizeDst = {(int32_t)width, (int32_t)height, 1};
1981 vk::ImageBlit imageBlitRegion{};
1982 imageBlitRegion.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1983 imageBlitRegion.srcSubresource.layerCount = 1;
1984 imageBlitRegion.srcOffsets[1] = blitSizeSrc;
1985 imageBlitRegion.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1986 imageBlitRegion.dstSubresource.layerCount = 1;
1987 imageBlitRegion.dstOffsets[1] = blitSizeDst;
1988 cmdBuffer.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, dstImage, vk::ImageLayout::eTransferDstOptimal, 1, &imageBlitRegion, vk::Filter::eLinear);
1989 src = dstImage;
1990 cmdImageMemoryBarrier(cmdBuffer, dstImage, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eTransferSrcOptimal);
1991 } else {
1992 src = image;
1993 }
1994
1995 vk::BufferImageCopy copyRegion{};
1996 copyRegion.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1997 copyRegion.imageSubresource.layerCount = 1;
1998 copyRegion.imageExtent = vk::Extent3D{width, height, 1};
1999 cmdBuffer.copyImageToBuffer(src, vk::ImageLayout::eTransferSrcOptimal, readBuffer.buffer, 1, &copyRegion);
2000
2001 vk::BufferMemoryBarrier2 hostBarrier{vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite, vk::PipelineStageFlagBits2::eHost, vk::AccessFlagBits2::eHostRead, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, readBuffer.buffer, 0, VK_WHOLE_SIZE};
2002 cmdBuffer.pipelineBarrier2(vk::DependencyInfo{{}, 0, nullptr, 1, &hostBarrier, 0, nullptr});
2003 cmdImageMemoryBarrier(cmdBuffer, image, srcStage, vk::AccessFlagBits2::eNone, vk::PipelineStageFlagBits2::eColorAttachmentOutput | vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eNone, srcLayout, layout); // Back to the layout the following passes expect
2005
2006 const bool swapRB = format == vk::Format::eR8G8B8A8Unorm || format == vk::Format::eR8G8B8A8Srgb; // Raw copy in swapchain format, the BMP needs BGRA
2007 const char* data = (const char*)readBuffer.mapped;
2008 for (uint32_t i = 0; i < height; i++) { // BMP rows are bottom-up
2009 char* dst = pixels.data() + i * width * bytesPerPixel;
2010 memcpy(dst, data + (height - i - 1) * width * bytesPerPixel, width * bytesPerPixel);
2011 if (swapRB) {
2012 for (uint32_t j = 0; j < width; j++) {
2013 std::swap(dst[j * bytesPerPixel], dst[j * bytesPerPixel + 2]);
2014 }
2015 }
2016 }
2017 clearBuffer(readBuffer);
2018 if (scaled) {
2019 if (dstView) {
2020 mDevice.destroyImageView(dstView, nullptr);
2021 }
2022 mDevice.freeMemory(dstImageMemory, nullptr);
2023 mDevice.destroyImage(dstImage, nullptr);
2024 }
2025}
2026
2028{
2029 return 32;
2030}
2031
uint64_t vertex
Definition RawEventData.h:9
int32_t i
#define CHKERR(cmd)
#define LOAD_SHADER(file, ext)
uint32_t iSector
int32_t retVal
GPUChain * chain
HMM_INLINE hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far)
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
Class for time synchronization of RawReader instances.
void OpenGLPrint(const char *s, float x, float y, float *color, float scale) override
std::vector< vk::CommandBuffer > mCommandBuffersMix
std::vector< vk::CommandBuffer > mCommandBuffers
std::vector< vk::Semaphore > mPresentSemaphore
std::vector< vk::Image > mRenderTargetImage
std::vector< vk::Pipeline > mPipelines
void resizeScene(uint32_t width, uint32_t height) override
void submitSingleTimeCommandBuffer(vk::CommandBuffer commandBuffer)
std::vector< VulkanImage > mDownsampleImages
void writeToBuffer(VulkanBuffer &buffer, size_t size, const void *srcData)
std::vector< vk::ImageView > mSwapChainImageViews
std::vector< vk::CommandBufferSubmitInfo > mFrameCommandBuffers
void pointSizeFactor(float factor) override
std::vector< FontSymbolVulkan > mFontSymbols
std::vector< vk::ImageView * > mRenderTargetView
bool prepareDraw(const hmm_mat4 &proj, const hmm_mat4 &view, bool requestScreenshot, bool toMixBuffer, float includeMixImage) override
void addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void *data) override
void mixImages(vk::CommandBuffer cmdBuffer, float mixSlaveImage)
void recreateRendering(bool forScreenshot=false, bool forMixing=false)
std::vector< VulkanBuffer > mFontVertexBuffer
double checkDevice(vk::PhysicalDevice device, const std::vector< const char * > &reqDeviceExtensions, const FeatureChain &reqDeviceFeatures, bool silent=false)
void endFillCommandBuffer(vk::CommandBuffer &commandBuffer)
uint32_t drawVertices(const vboList &v, const drawType t) override
std::vector< vk::CommandBuffer > mCommandBuffersPresent
std::vector< vk::CommandBuffer > mCommandBuffersDownsample
void ActivateColor(std::array< float, 4 > &color) override
uint64_t queueSubmit(uint64_t *timelineSignalValue, Args &&... args)
std::vector< VulkanBuffer > mUniformBuffersMat[3]
void updateSwapChainDetails(const vk::PhysicalDevice &device)
void handleSwapChainResult(vk::Result retVal, uint8_t suboptimalBit)
vk::StructureChain< vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan11Features, vk::PhysicalDeviceVulkan12Features, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceVulkan14Features, vk::PhysicalDeviceSwapchainMaintenance1FeaturesKHR, vk::PhysicalDevicePresentIdFeaturesKHR, vk::PhysicalDevicePresentWaitFeaturesKHR > FeatureChain
void finishDraw(bool doScreenshot, bool toMixBuffer, float includeMixImage) override
void writeToImage(VulkanImage &image, const void *srcData, size_t srcSize)
std::unordered_map< std::string, vk::ShaderModuleCreateInfo > mShaders
vk::DescriptorSetLayout mUniformDescriptorTexture
void downsampleToFramebuffer(vk::CommandBuffer &commandBuffer)
void createSwapChain(vk::SwapchainKHR oldSwapChain={})
vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR &capabilities)
VulkanImage createImage(uint32_t sizex, uint32_t sizey, const void *srcData=nullptr, size_t srcSize=0, vk::Format format=vk::Format::eR8G8B8A8Srgb)
std::vector< TextDrawCommand > mTextDrawCommands
void cmdPushDescriptors(vk::CommandBuffer commandBuffer, vk::PipelineLayout layout, const VulkanBuffer &ubo, vk::ImageView textureView={})
void finishFrame(bool doScreenshot, bool toMixBuffer, float includeMixImage) override
void clearSwapChain(bool keepSwapChain=false)
std::vector< vk::Semaphore > mImageAvailableSemaphore
void startFillCommandBuffer(vk::CommandBuffer &commandBuffer, uint32_t imageIndex, bool toMixBuffer=false)
std::vector< vk::CommandBuffer > mCommandBuffersTexture
vk::DebugUtilsMessengerEXT mDebugMessenger
void cmdDownsample(vk::CommandBuffer commandBuffer, vk::ImageView src, vk::ImageView dst, uint32_t width, uint32_t height, int32_t factor)
VulkanBuffer createBuffer(size_t size, const void *srcData=nullptr, vk::BufferUsageFlags type=vk::BufferUsageFlagBits::eVertexBuffer, int32_t deviceMemory=1)
void createOffscreenBuffers(bool forScreenshot=false, bool forMixing=false)
void lineWidthFactor(float factor) override
void readImageToPixels(vk::Image image, vk::ImageView view, vk::ImageLayout layout, std::vector< char > &pixels)
std::vector< vk::CommandBuffer > mCommandBuffersText
void loadDataToGPU(size_t totalVertizes) override
vecpod< DrawArraysIndirectCommand > mCmdBuffer
std::vector< int32_t > mIndirectSectorOffset
static constexpr float SMOOTH_LINE_EXTRA_WIDTH
std::vector< char > mScreenshotPixels
static constexpr float SMOOTH_POINT_EXTRA_SIZE
float getDownsampleFactor(bool screenshot=false)
std::tuple< uint32_t, uint32_t, int32_t > vboList
virtual uint32_t getReqVulkanExtensions(const char **&p)
virtual void getSize(int32_t &width, int32_t &height)
virtual int32_t getVulkanSurface(void *instance, void *surface)
const GPUSettingsDisplayLight & cfgL() const
Definition GPUDisplay.h:69
const GPUParam * param()
Definition GPUDisplay.h:83
int32_t updateRenderPipeline() const
Definition GPUDisplay.h:74
const GPUSettingsDisplayRenderer & cfgR() const
Definition GPUDisplay.h:68
vecpod< vtx > * vertexBuffer()
Definition GPUDisplay.h:82
bool drawTextInCompatMode() const
Definition GPUDisplay.h:86
int32_t updateDrawCommands() const
Definition GPUDisplay.h:73
GPUDisplayFrontend * frontend()
Definition GPUDisplay.h:85
const vecpod< uint32_t > * vertexBufferCount() const
Definition GPUDisplay.h:77
const GPUSettingsDisplay & cfg() const
Definition GPUDisplay.h:71
vecpod< int32_t > * vertexBufferStart()
Definition GPUDisplay.h:76
const GPUSettingsProcessing & GetProcessingSettings() const
Definition GPUDisplay.h:84
GLeglImageOES image
Definition glcorearb.h:4021
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum func
Definition glcorearb.h:778
GLenum mode
Definition glcorearb.h:266
GLenum src
Definition glcorearb.h:1767
GLint GLsizei count
Definition glcorearb.h:399
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLuint color
Definition glcorearb.h:1272
GLuint GLsizei const GLuint const GLintptr * offsets
Definition glcorearb.h:2595
const GLdouble * v
Definition glcorearb.h:832
GLsizei GLenum GLenum * types
Definition glcorearb.h:2516
GLint GLsizei GLsizei height
Definition glcorearb.h:270
GLint first
Definition glcorearb.h:399
GLint GLsizei width
Definition glcorearb.h:270
GLdouble f
Definition glcorearb.h:310
GLint y
Definition glcorearb.h:270
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLenum GLenum dst
Definition glcorearb.h:1767
GLboolean * data
Definition glcorearb.h:298
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
Definition glcorearb.h:275
GLuint GLfloat * val
Definition glcorearb.h:1582
GLint GLint GLsizei GLsizei GLsizei depth
Definition glcorearb.h:470
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
GLuint memory
Definition glcorearb.h:5234
GLsizeiptr const void GLenum usage
Definition glcorearb.h:659
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector