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
19#include "GPUCommonDef.h"
21#include "GPUDisplay.h"
22#include "GPULogging.h"
23#include "GPUParam.h"
24
25#include <mutex>
26
27using namespace o2::gpu;
28
30QGET_LD_BINARY_SYMBOLS(shaders_shaders_vertex_vert_spv);
31QGET_LD_BINARY_SYMBOLS(shaders_shaders_fragment_frag_spv);
32QGET_LD_BINARY_SYMBOLS(shaders_shaders_vertexPoint_vert_spv);
33QGET_LD_BINARY_SYMBOLS(shaders_shaders_vertexTexture_vert_spv);
34QGET_LD_BINARY_SYMBOLS(shaders_shaders_fragmentTexture_frag_spv);
35QGET_LD_BINARY_SYMBOLS(shaders_shaders_fragmentText_frag_spv);
36
37// #define CHKERR(cmd) {cmd;}
38#define CHKERR(cmd) \
39 do { \
40 auto tmp_internal_retVal = cmd; \
41 if ((int32_t)tmp_internal_retVal < 0) { \
42 GPUError("VULKAN ERROR: %d: %s (%s: %d)", (int32_t)tmp_internal_retVal, "ERROR", __FILE__, __LINE__); \
43 throw std::runtime_error("Vulkan Failure"); \
44 } \
45 } while (false)
46
53
54// ---------------------------- VULKAN HELPERS ----------------------------
55
56static int32_t checkVulkanLayersSupported(const std::vector<const char*>& validationLayers)
57{
58 std::vector<vk::LayerProperties> availableLayers = vk::enumerateInstanceLayerProperties();
59 for (const char* layerName : validationLayers) {
60 bool layerFound = false;
61
62 for (const auto& layerProperties : availableLayers) {
63 if (strcmp(layerName, layerProperties.layerName) == 0) {
64 layerFound = true;
65 break;
66 }
67 }
68
69 if (!layerFound) {
70 return 1;
71 }
72 }
73 return 0;
74}
75
76static uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties, vk::PhysicalDevice physDev)
77{
78 vk::PhysicalDeviceMemoryProperties memProperties = physDev.getMemoryProperties();
79
80 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
81 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
82 return i;
83 }
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
99static vk::PresentModeKHR chooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& availablePresentModes, vk::PresentModeKHR desiredMode = vk::PresentModeKHR::eMailbox)
100{
101 for (const auto& availablePresentMode : availablePresentModes) {
102 if (availablePresentMode == desiredMode) {
103 return availablePresentMode;
104 }
105 }
106 static bool errorShown = false;
107 if (!errorShown) {
108 errorShown = true;
109 GPUError("VULKAN ERROR: Desired present mode not available, using FIFO mode");
110 }
111 return vk::PresentModeKHR::eFifo;
112}
113
114vk::Extent2D GPUDisplayBackendVulkan::chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities)
115{
116 if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
117 return capabilities.currentExtent;
118 } else {
119 int32_t width, height;
121 vk::Extent2D actualExtent = {(uint32_t)width, (uint32_t)height};
122 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
123 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
124 return actualExtent;
125 }
126}
127
128static vk::ShaderModule createShaderModule(const char* code, size_t size, vk::Device device)
129{
130 vk::ShaderModuleCreateInfo createInfo{};
131 createInfo.codeSize = size;
132 createInfo.pCode = reinterpret_cast<const uint32_t*>(code);
133 return device.createShaderModule(createInfo, nullptr);
134}
135
136static void cmdImageMemoryBarrier(vk::CommandBuffer cmdbuffer, vk::Image image, vk::AccessFlags srcAccessMask, vk::AccessFlags dstAccessMask, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::PipelineStageFlags srcStageMask, vk::PipelineStageFlags dstStageMask)
137{
138 vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
139 vk::ImageMemoryBarrier barrier{};
140 barrier.srcAccessMask = srcAccessMask;
141 barrier.dstAccessMask = dstAccessMask;
142 barrier.oldLayout = oldLayout;
143 barrier.newLayout = newLayout;
144 barrier.image = image;
145 barrier.subresourceRange = range;
146 cmdbuffer.pipelineBarrier(srcStageMask, dstStageMask, {}, 0, nullptr, 0, nullptr, 1, &barrier);
147}
148
149void GPUDisplayBackendVulkan::updateSwapChainDetails(const vk::PhysicalDevice& device)
150{
151 mSwapChainDetails.capabilities = device.getSurfaceCapabilitiesKHR(mSurface);
152 mSwapChainDetails.formats = device.getSurfaceFormatsKHR(mSurface);
153 mSwapChainDetails.presentModes = device.getSurfacePresentModesKHR(mSurface);
154}
155
157{
158 vk::CommandBufferAllocateInfo allocInfo{};
159 allocInfo.level = vk::CommandBufferLevel::ePrimary;
160 allocInfo.commandPool = mCommandPool;
161 allocInfo.commandBufferCount = 1;
162 vk::CommandBuffer commandBuffer = mDevice.allocateCommandBuffers(allocInfo)[0];
163 vk::CommandBufferBeginInfo beginInfo{};
164 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
165 commandBuffer.begin(beginInfo);
166 return commandBuffer;
167}
168
170{
171 commandBuffer.end();
172 vk::SubmitInfo submitInfo{};
173 submitInfo.commandBufferCount = 1;
174 submitInfo.pCommandBuffers = &commandBuffer;
175 static std::mutex fenceMutex;
176 {
177 std::lock_guard<std::mutex> guard(fenceMutex);
178 CHKERR(mGraphicsQueue.submit(1, &submitInfo, mSingleCommitFence));
179 CHKERR(mDevice.waitForFences(1, &mSingleCommitFence, true, UINT64_MAX));
180 CHKERR(mDevice.resetFences(1, &mSingleCommitFence));
181 }
182 mDevice.freeCommandBuffers(mCommandPool, 1, &commandBuffer);
183}
184
185static vk::ImageView createImageViewI(vk::Device device, vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor, uint32_t mipLevels = 1)
186{
187 vk::ImageViewCreateInfo viewInfo{};
188 viewInfo.image = image;
189 viewInfo.viewType = vk::ImageViewType::e2D;
190 viewInfo.format = format;
191 viewInfo.subresourceRange.aspectMask = aspectFlags;
192 viewInfo.subresourceRange.baseMipLevel = 0;
193 viewInfo.subresourceRange.levelCount = mipLevels;
194 viewInfo.subresourceRange.baseArrayLayer = 0;
195 viewInfo.subresourceRange.layerCount = 1;
196 return device.createImageView(viewInfo, nullptr);
197}
198
199static 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)
200{
201 vk::ImageCreateInfo imageInfo{};
202 imageInfo.imageType = vk::ImageType::e2D;
203 imageInfo.extent.width = width;
204 imageInfo.extent.height = height;
205 imageInfo.extent.depth = 1;
206 imageInfo.mipLevels = mipLevels;
207 imageInfo.arrayLayers = 1;
208 imageInfo.format = format;
209 imageInfo.tiling = tiling;
210 imageInfo.initialLayout = layout;
211 imageInfo.usage = usage;
212 imageInfo.samples = numSamples;
213 imageInfo.sharingMode = vk::SharingMode::eExclusive;
214 image = device.createImage(imageInfo);
215
216 vk::MemoryRequirements memRequirements;
217 memRequirements = device.getImageMemoryRequirements(image);
218
219 vk::MemoryAllocateInfo allocInfo{};
220 allocInfo.allocationSize = memRequirements.size;
221 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties, physicalDevice);
222 imageMemory = device.allocateMemory(allocInfo, nullptr);
223
224 device.bindImageMemory(image, imageMemory, 0);
225}
226
227static uint32_t getMaxUsableSampleCount(vk::PhysicalDeviceProperties& physicalDeviceProperties)
228{
229 vk::SampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
230 if (counts & vk::SampleCountFlagBits::e64) {
231 return 64;
232 } else if (counts & vk::SampleCountFlagBits::e32) {
233 return 32;
234 } else if (counts & vk::SampleCountFlagBits::e16) {
235 return 16;
236 } else if (counts & vk::SampleCountFlagBits::e8) {
237 return 8;
238 } else if (counts & vk::SampleCountFlagBits::e4) {
239 return 4;
240 } else if (counts & vk::SampleCountFlagBits::e2) {
241 return 2;
242 }
243 return 1;
244}
245
246static vk::SampleCountFlagBits getMSAASamplesFlag(uint32_t msaa)
247{
248 if (msaa == 2) {
249 return vk::SampleCountFlagBits::e2;
250 } else if (msaa == 4) {
251 return vk::SampleCountFlagBits::e4;
252 } else if (msaa == 8) {
253 return vk::SampleCountFlagBits::e8;
254 } else if (msaa == 16) {
255 return vk::SampleCountFlagBits::e16;
256 } else if (msaa == 32) {
257 return vk::SampleCountFlagBits::e32;
258 } else if (msaa == 64) {
259 return vk::SampleCountFlagBits::e64;
260 }
261 return vk::SampleCountFlagBits::e1;
262}
263
264template <class T, class S>
265static inline void clearVector(T& v, S func, bool downsize = true)
266{
267 std::for_each(v.begin(), v.end(), func);
268 if (downsize) {
269 v.clear();
270 }
271}
272
273// ---------------------------- VULKAN DEVICE MANAGEMENT ----------------------------
274
275double GPUDisplayBackendVulkan::checkDevice(vk::PhysicalDevice device, const std::vector<const char*>& reqDeviceExtensions)
276{
277 double score = -1.;
278 vk::PhysicalDeviceProperties deviceProperties = device.getProperties();
279 vk::PhysicalDeviceFeatures deviceFeatures = device.getFeatures();
280 vk::PhysicalDeviceMemoryProperties memoryProperties = device.getMemoryProperties();
281 if (!deviceFeatures.geometryShader || !deviceFeatures.wideLines || !deviceFeatures.largePoints) {
282 return (-1);
283 }
284
285 std::vector<vk::QueueFamilyProperties> queueFamilies = device.getQueueFamilyProperties();
286 bool found = false;
287 for (uint32_t i = 0; i < queueFamilies.size(); i++) {
288 if (!(queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics)) {
289 return (-1);
290 }
291 vk::Bool32 presentSupport = device.getSurfaceSupportKHR(i, mSurface);
292 if (!presentSupport) {
293 return (-1);
294 }
296 found = true;
297 break;
298 }
299 if (!found) {
300 GPUInfo("%s ignored due to missing queue properties", &deviceProperties.deviceName[0]);
301 return (-1);
302 }
303
304 std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties(nullptr);
305 uint32_t extensionsFound = 0;
306 for (uint32_t i = 0; i < reqDeviceExtensions.size(); i++) {
307 for (uint32_t j = 0; j < availableExtensions.size(); j++) {
308 if (strcmp(reqDeviceExtensions[i], availableExtensions[j].extensionName) == 0) {
309 extensionsFound++;
310 break;
311 }
312 }
313 }
314 if (extensionsFound < reqDeviceExtensions.size()) {
315 GPUInfo("%s ignored due to missing extensions", &deviceProperties.deviceName[0]);
316 return (-1);
317 }
318
321 GPUInfo("%s ignored due to incompatible swap chain", &deviceProperties.deviceName[0]);
322 return (-1);
323 }
324
325 score = 1;
326 if (deviceProperties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
327 score += 1e12;
328 } else if (deviceProperties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) {
329 score += 1e11;
330 }
331
332 for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; i++) {
333 if (memoryProperties.memoryHeaps[i].flags & vk::MemoryHeapFlagBits::eDeviceLocal) {
334 score += memoryProperties.memoryHeaps[i].size;
335 }
336 }
337
338 return score;
339}
340
342{
343 VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
344 vk::ApplicationInfo appInfo{};
345 appInfo.pApplicationName = "GPU CA Standalone display";
346 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
347 appInfo.pEngineName = "GPU CI Standalone Engine";
348 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
349 appInfo.apiVersion = VK_API_VERSION_1_0;
350
351 vk::InstanceCreateInfo instanceCreateInfo;
352 instanceCreateInfo.pApplicationInfo = &appInfo;
353
354 const char** frontendExtensions;
355 uint32_t frontendExtensionCount = mDisplay->frontend()->getReqVulkanExtensions(frontendExtensions);
356 std::vector<const char*> reqInstanceExtensions(frontendExtensions, frontendExtensions + frontendExtensionCount);
357
358 const std::vector<const char*> reqValidationLayers = {"VK_LAYER_KHRONOS_validation"};
359 auto debugCallback = [](vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType, const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) -> VkBool32 {
360 static int32_t throwOnError = getenv("GPUCA_VULKAN_VALIDATION_THROW") ? atoi(getenv("GPUCA_VULKAN_VALIDATION_THROW")) : 0;
361 static bool showVulkanValidationInfo = getenv("GPUCA_VULKAN_VALIDATION_INFO") && atoi(getenv("GPUCA_VULKAN_VALIDATION_INFO"));
362 switch (messageSeverity) {
363 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose:
364 if (showVulkanValidationInfo) {
365 GPUInfo("%s", pCallbackData->pMessage);
366 }
367 break;
368 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning:
369 GPUWarning("%s", pCallbackData->pMessage);
370 if (throwOnError > 1) {
371 throw std::logic_error("break_on_validation_warning");
372 }
373 break;
374 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError:
375 GPUError("%s", pCallbackData->pMessage);
376 if (throwOnError) {
377 throw std::logic_error("break_on_validation_error");
378 }
379 break;
380 case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo:
381 default:
382 GPUInfo("%s", pCallbackData->pMessage);
383 break;
384 }
385 return false;
386 };
387 vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
389 if (checkVulkanLayersSupported(reqValidationLayers)) {
390 throw std::runtime_error("Requested validation layer support not available");
391 }
392 reqInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
393 instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(reqValidationLayers.size());
394 instanceCreateInfo.ppEnabledLayerNames = reqValidationLayers.data();
395 instanceCreateInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
396
397 debugCreateInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
398 debugCreateInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
399 debugCreateInfo.pfnUserCallback = debugCallback;
400 debugCreateInfo.pUserData = nullptr;
401 } else {
402 instanceCreateInfo.enabledLayerCount = 0;
403 }
404
405 instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(reqInstanceExtensions.size());
406 instanceCreateInfo.ppEnabledExtensionNames = reqInstanceExtensions.data();
407
408 mInstance = vk::createInstance(instanceCreateInfo, nullptr);
409 VULKAN_HPP_DEFAULT_DISPATCHER.init(mInstance);
410
412 GPUInfo("Enabling Vulkan Validation Layers");
413 mDebugMessenger = mInstance.createDebugUtilsMessengerEXT(debugCreateInfo, nullptr);
414 }
415 std::vector<vk::ExtensionProperties> extensions = vk::enumerateInstanceExtensionProperties(nullptr);
416 if (mDisplay->GetProcessingSettings().debugLevel >= 3) {
417 std::cout << "available instance extensions: " << extensions.size() << "\n";
418 for (const auto& extension : extensions) {
419 std::cout << '\t' << extension.extensionName << '\n';
420 }
421 }
422
424 throw std::runtime_error("Frontend does not provide Vulkan surface");
425 }
426
427 const std::vector<const char*> reqDeviceExtensions = {
428 VK_KHR_SWAPCHAIN_EXTENSION_NAME};
429
430 mPhysicalDevice = VkPhysicalDevice(VK_NULL_HANDLE);
431 std::vector<vk::PhysicalDevice> devices = mInstance.enumeratePhysicalDevices();
432 if (devices.size() == 0) {
433 throw std::runtime_error("No Vulkan device present!");
434 }
435 double bestScore = -1.;
436 for (uint32_t i = 0; i < devices.size(); i++) {
437 double score = checkDevice(devices[i], reqDeviceExtensions);
438 if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
439 vk::PhysicalDeviceProperties deviceProperties = devices[i].getProperties();
440 GPUInfo("Available Vulkan device %d: %s - Score %f", i, &deviceProperties.deviceName[0], score);
441 }
442 if (score > bestScore && score > 0) {
443 mPhysicalDevice = devices[i];
444 bestScore = score;
445 }
446 }
447 if (mDisplay->cfg().vulkan.forceDevice != -1) {
448 if (mDisplay->cfg().vulkan.forceDevice < 0 || mDisplay->cfg().vulkan.forceDevice >= (int32_t)devices.size()) {
449 throw std::runtime_error("Invalid Vulkan device selected");
450 }
451 mPhysicalDevice = devices[mDisplay->cfg().vulkan.forceDevice];
452 }
453 if (!mPhysicalDevice) {
454 throw std::runtime_error("All available Vulkan devices unsuited");
455 }
456
458 vk::PhysicalDeviceProperties deviceProperties = mPhysicalDevice.getProperties();
459 vk::PhysicalDeviceFeatures deviceFeatures = mPhysicalDevice.getFeatures();
460 vk::FormatProperties depth32FormatProperties = mPhysicalDevice.getFormatProperties(vk::Format::eD32Sfloat);
461 vk::FormatProperties depth64FormatProperties = mPhysicalDevice.getFormatProperties(vk::Format::eD32SfloatS8Uint);
462 vk::FormatProperties formatProperties = mPhysicalDevice.getFormatProperties(mSurfaceFormat.format);
463 GPUInfo("Using physical Vulkan device %s", &deviceProperties.deviceName[0]);
464 mMaxMSAAsupported = getMaxUsableSampleCount(deviceProperties);
465 mZSupported = (bool)(depth32FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
466 mStencilSupported = (bool)(depth64FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
467 mCubicFilterSupported = (bool)(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterCubicEXT);
468 bool mailboxSupported = std::find(mSwapChainDetails.presentModes.begin(), mSwapChainDetails.presentModes.end(), vk::PresentModeKHR::eMailbox) != mSwapChainDetails.presentModes.end();
469 if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
470 GPUInfo("Max MSAA: %d, 32 bit Z buffer %d, 32 bit Z buffer + stencil buffer %d, Cubic Filtering %d, Mailbox present mode %d\n", (int32_t)mMaxMSAAsupported, (int32_t)mZSupported, (int32_t)mStencilSupported, (int32_t)mCubicFilterSupported, (int32_t)mailboxSupported);
471 }
472
473 vk::DeviceQueueCreateInfo queueCreateInfo{};
474 queueCreateInfo.queueFamilyIndex = mGraphicsFamily;
475 queueCreateInfo.queueCount = 1;
476 float queuePriority = 1.0f;
477 queueCreateInfo.pQueuePriorities = &queuePriority;
478 vk::DeviceCreateInfo deviceCreateInfo{};
479 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
480 deviceCreateInfo.queueCreateInfoCount = 1;
481 deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
482 deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(reqDeviceExtensions.size());
483 deviceCreateInfo.ppEnabledExtensionNames = reqDeviceExtensions.data();
484 deviceCreateInfo.enabledLayerCount = instanceCreateInfo.enabledLayerCount;
485 deviceCreateInfo.ppEnabledLayerNames = instanceCreateInfo.ppEnabledLayerNames;
486 mDevice = mPhysicalDevice.createDevice(deviceCreateInfo, nullptr);
487 VULKAN_HPP_DEFAULT_DISPATCHER.init(mDevice);
489
490 vk::CommandPoolCreateInfo poolInfo{};
491 poolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
492 poolInfo.queueFamilyIndex = mGraphicsFamily;
493 mCommandPool = mDevice.createCommandPool(poolInfo, nullptr);
494}
495
497{
498 mDevice.destroyCommandPool(mCommandPool, nullptr);
499 mDevice.destroy(nullptr);
500 mInstance.destroySurfaceKHR(mSurface, nullptr);
502 mInstance.destroyDebugUtilsMessengerEXT(mDebugMessenger, nullptr);
503 }
504}
505
506// ---------------------------- VULKAN COMMAND BUFFERS ----------------------------
507
509{
510 vk::CommandBufferAllocateInfo allocInfo{};
511 allocInfo.commandPool = mCommandPool;
512 allocInfo.level = vk::CommandBufferLevel::ePrimary;
513 allocInfo.commandBufferCount = mFramesInFlight;
515 mCommandBuffers = mDevice.allocateCommandBuffers(allocInfo);
516 mCommandBuffersText = mDevice.allocateCommandBuffers(allocInfo);
517 mCommandBuffersTexture = mDevice.allocateCommandBuffers(allocInfo);
518 mCommandBuffersDownsample = mDevice.allocateCommandBuffers(allocInfo);
519 mCommandBuffersMix = mDevice.allocateCommandBuffers(allocInfo);
520}
521
523{
524 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffers.size(), mCommandBuffers.data());
525 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersText.size(), mCommandBuffersText.data());
526 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersTexture.size(), mCommandBuffersTexture.data());
528 mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersMix.size(), mCommandBuffersMix.data());
529}
530
531// ---------------------------- VULKAN SEMAPHORES AND FENCES ----------------------------
532
534{
535 vk::SemaphoreCreateInfo semaphoreInfo{};
536 vk::FenceCreateInfo fenceInfo{};
537 fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
544 for (uint32_t i = 0; i < mFramesInFlight; i++) {
545 mImageAvailableSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
546 mRenderFinishedSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
547 mTextFinishedSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
548 mMixFinishedSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
549 mDownsampleFinishedSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr);
550 mInFlightFence[i] = mDevice.createFence(fenceInfo, nullptr);
551 }
552 fenceInfo.flags = {};
553 mSingleCommitFence = mDevice.createFence(fenceInfo, nullptr);
554}
555
557{
558 clearVector(mImageAvailableSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
559 clearVector(mRenderFinishedSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
560 clearVector(mTextFinishedSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
561 clearVector(mMixFinishedSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
562 clearVector(mDownsampleFinishedSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); });
563 clearVector(mInFlightFence, [&](auto& x) { mDevice.destroyFence(x, nullptr); });
564 mDevice.destroyFence(mSingleCommitFence, nullptr);
565}
566
567// ---------------------------- VULKAN UNIFORM LAYOUTS AND BUFFERS ----------------------------
568
570{
571 for (int32_t j = 0; j < 3; j++) {
574 for (uint32_t i = 0; i < mFramesInFlight; i++) {
575 mUniformBuffersMat[j][i] = createBuffer(sizeof(hmm_mat4), nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0);
576 mUniformBuffersCol[j][i] = createBuffer(sizeof(float) * 4, nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0);
577 }
578 }
579
580 std::array<vk::DescriptorPoolSize, 2> poolSizes{};
581 poolSizes[0].type = vk::DescriptorType::eUniformBuffer;
582 poolSizes[0].descriptorCount = (uint32_t)mFramesInFlight * (2 * 3);
583 poolSizes[1].type = vk::DescriptorType::eCombinedImageSampler;
584 poolSizes[1].descriptorCount = (uint32_t)mFramesInFlight * 2;
585 vk::DescriptorPoolCreateInfo poolInfo{};
586 poolInfo.poolSizeCount = poolSizes.size();
587 poolInfo.pPoolSizes = poolSizes.data();
588 poolInfo.maxSets = (uint32_t)mFramesInFlight * 3;
589 mDescriptorPool = mDevice.createDescriptorPool(poolInfo, nullptr);
590
591 vk::DescriptorSetLayoutBinding uboLayoutBindingMat{};
592 uboLayoutBindingMat.binding = 0;
593 uboLayoutBindingMat.descriptorType = vk::DescriptorType::eUniformBuffer;
594 uboLayoutBindingMat.descriptorCount = 1;
595 uboLayoutBindingMat.stageFlags = vk::ShaderStageFlagBits::eVertex;
596 vk::DescriptorSetLayoutBinding uboLayoutBindingCol = uboLayoutBindingMat;
597 uboLayoutBindingCol.binding = 1;
598 uboLayoutBindingCol.stageFlags = vk::ShaderStageFlagBits::eFragment;
599 vk::DescriptorSetLayoutBinding samplerLayoutBinding{};
600 samplerLayoutBinding.binding = 2;
601 samplerLayoutBinding.descriptorCount = 1;
602 samplerLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
603 samplerLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
604 vk::DescriptorSetLayoutBinding bindings[3] = {uboLayoutBindingMat, uboLayoutBindingCol, samplerLayoutBinding};
605
606 vk::DescriptorSetLayoutCreateInfo layoutInfo{};
607 layoutInfo.bindingCount = 2;
608 layoutInfo.pBindings = bindings;
609 mUniformDescriptor = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
610 layoutInfo.bindingCount = 3;
611 mUniformDescriptorTexture = mDevice.createDescriptorSetLayout(layoutInfo, nullptr);
612
613 vk::DescriptorSetAllocateInfo allocInfo{};
614 allocInfo.descriptorPool = mDescriptorPool;
615 allocInfo.descriptorSetCount = (uint32_t)mFramesInFlight;
616 for (int32_t j = 0; j < 3; j++) { // 0 = Render, 1 = Text, 2 = Texture
617 std::vector<vk::DescriptorSetLayout> layouts(mFramesInFlight, j ? mUniformDescriptorTexture : mUniformDescriptor);
618 allocInfo.pSetLayouts = layouts.data();
619 mDescriptorSets[j] = mDevice.allocateDescriptorSets(allocInfo);
620
621 for (int32_t k = 0; k < 2; k++) {
622 auto& mUniformBuffers = k ? mUniformBuffersCol[j] : mUniformBuffersMat[j];
623 for (uint32_t i = 0; i < mFramesInFlight; i++) {
624 vk::DescriptorBufferInfo bufferInfo{};
625 bufferInfo.buffer = mUniformBuffers[i].buffer;
626 bufferInfo.offset = 0;
627 bufferInfo.range = mUniformBuffers[i].size;
628
629 vk::WriteDescriptorSet descriptorWrite{};
630 descriptorWrite.dstSet = mDescriptorSets[j][i];
631 descriptorWrite.dstBinding = k;
632 descriptorWrite.dstArrayElement = 0;
633 descriptorWrite.descriptorType = vk::DescriptorType::eUniformBuffer;
634 descriptorWrite.descriptorCount = 1;
635 descriptorWrite.pBufferInfo = &bufferInfo;
636 descriptorWrite.pImageInfo = nullptr;
637 descriptorWrite.pTexelBufferView = nullptr;
638 mDevice.updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
639 }
640 }
641 }
642
645 }
646}
647
649{
650 mDevice.destroyDescriptorSetLayout(mUniformDescriptor, nullptr);
651 mDevice.destroyDescriptorSetLayout(mUniformDescriptorTexture, nullptr);
652 mDevice.destroyDescriptorPool(mDescriptorPool, nullptr);
653 for (int32_t j = 0; j < 3; j++) {
654 clearVector(mUniformBuffersMat[j], [&](auto& x) { clearBuffer(x); });
655 clearVector(mUniformBuffersCol[j], [&](auto& x) { clearBuffer(x); });
656 }
657}
658
659void GPUDisplayBackendVulkan::setMixDescriptor(int32_t descriptorIndex, int32_t imageIndex)
660{
661 vk::DescriptorImageInfo imageInfo{};
662 imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
663 imageInfo.sampler = mTextureSampler;
664 imageInfo.imageView = *mRenderTargetView[imageIndex + mImageCount];
665 vk::WriteDescriptorSet descriptorWrite{};
666 descriptorWrite.dstSet = mDescriptorSets[2][descriptorIndex];
667 descriptorWrite.dstBinding = 2;
668 descriptorWrite.dstArrayElement = 0;
669 descriptorWrite.descriptorType = vk::DescriptorType::eCombinedImageSampler;
670 descriptorWrite.descriptorCount = 1;
671 descriptorWrite.pImageInfo = &imageInfo;
672 mDevice.updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
673}
674
675// ---------------------------- VULKAN TEXTURE SAMPLER ----------------------------
676
678{
679 vk::SamplerCreateInfo samplerInfo{};
680 samplerInfo.magFilter = vk::Filter::eLinear;
681 samplerInfo.minFilter = vk::Filter::eLinear;
682 samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
683 samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
684 samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
685 samplerInfo.compareEnable = false;
686 samplerInfo.compareOp = vk::CompareOp::eAlways;
687 samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
688 samplerInfo.unnormalizedCoordinates = false;
689 samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
690 samplerInfo.mipLodBias = 0.0f;
691 samplerInfo.minLod = 0.0f;
692 samplerInfo.maxLod = 0.0f;
693 mTextureSampler = mDevice.createSampler(samplerInfo, nullptr);
694}
695
697{
698 mDevice.destroySampler(mTextureSampler, nullptr);
699}
700
701// ---------------------------- VULKAN SWAPCHAIN MANAGEMENT ----------------------------
702
703void GPUDisplayBackendVulkan::createSwapChain(bool forScreenshot, bool forMixing)
704{
705 mDownsampleFactor = getDownsampleFactor(forScreenshot);
707 mSwapchainImageReadable = forScreenshot;
708
710 mSurfaceFormat = chooseSwapSurfaceFormat(mSwapChainDetails.formats);
711 mPresentMode = chooseSwapPresentMode(mSwapChainDetails.presentModes, mDisplay->cfgR().drawQualityVSync ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate);
712 vk::Extent2D extent = chooseSwapExtent(mSwapChainDetails.capabilities);
713
714 uint32_t imageCount = mSwapChainDetails.capabilities.minImageCount + 1;
715 if (mSwapChainDetails.capabilities.maxImageCount > 0 && imageCount > mSwapChainDetails.capabilities.maxImageCount) {
716 imageCount = mSwapChainDetails.capabilities.maxImageCount;
717 }
718
719 mScreenWidth = extent.width;
720 mScreenHeight = extent.height;
723
724 vk::SwapchainCreateInfoKHR swapCreateInfo{};
725 swapCreateInfo.surface = mSurface;
726 swapCreateInfo.minImageCount = imageCount;
727 swapCreateInfo.imageFormat = mSurfaceFormat.format;
728 swapCreateInfo.imageColorSpace = mSurfaceFormat.colorSpace;
729 swapCreateInfo.imageExtent = extent;
730 swapCreateInfo.imageArrayLayers = 1;
731 swapCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
732 swapCreateInfo.imageSharingMode = vk::SharingMode::eExclusive;
733 swapCreateInfo.queueFamilyIndexCount = 0; // Optional
734 swapCreateInfo.pQueueFamilyIndices = nullptr; // Optional
735 swapCreateInfo.preTransform = mSwapChainDetails.capabilities.currentTransform;
736 swapCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
737 swapCreateInfo.presentMode = mPresentMode;
738 swapCreateInfo.clipped = true;
739 swapCreateInfo.oldSwapchain = VkSwapchainKHR(VK_NULL_HANDLE);
741 swapCreateInfo.imageUsage |= vk::ImageUsageFlagBits::eTransferSrc;
742 }
743 if (mDownsampleFSAA) {
744 swapCreateInfo.imageUsage |= vk::ImageUsageFlagBits::eTransferDst;
745 }
746 mSwapChain = mDevice.createSwapchainKHR(swapCreateInfo, nullptr);
747
748 mSwapChainImages = mDevice.getSwapchainImagesKHR(mSwapChain);
749 uint32_t oldFramesInFlight = mFramesInFlight;
751 mFramesInFlight = mDisplay->cfg().vulkan.nFramesInFlight == 0 ? mImageCount : mDisplay->cfg().vulkan.nFramesInFlight;
753
754 if (mFramesInFlight > oldFramesInFlight || !mCommandInfrastructureCreated) {
759 }
764 }
765
767 for (uint32_t i = 0; i < mImageCount; i++) {
768 mSwapChainImageViews[i] = createImageViewI(mDevice, mSwapChainImages[i], mSurfaceFormat.format);
769 }
770}
771
773{
774 clearVector(mSwapChainImageViews, [&](auto& x) { mDevice.destroyImageView(x, nullptr); });
775 mDevice.destroySwapchainKHR(mSwapChain, nullptr);
776}
777
778void GPUDisplayBackendVulkan::recreateRendering(bool forScreenshot, bool forMixing)
779{
780 mDevice.waitIdle();
781 bool needUpdateSwapChain = mMustUpdateSwapChain || mDownsampleFactor != getDownsampleFactor(forScreenshot) || mSwapchainImageReadable != forScreenshot;
782 bool needUpdateOffscreenBuffers = needUpdateSwapChain || mMSAASampleCount != getMSAASamplesFlag(std::min<uint32_t>(mMaxMSAAsupported, mDisplay->cfgR().drawQualityMSAA)) || mZActive != (mZSupported && mDisplay->cfgL().depthBuffer) || mMixingSupported != forMixing;
784 if (needUpdateOffscreenBuffers) {
786 if (needUpdateSwapChain) {
788 createSwapChain(forScreenshot, forMixing);
789 }
790 createOffscreenBuffers(forScreenshot, forMixing);
791 }
794}
795
796// ---------------------------- VULKAN OFFSCREEN BUFFERS ----------------------------
797
798void GPUDisplayBackendVulkan::createOffscreenBuffers(bool forScreenshot, bool forMixing)
799{
800 mMSAASampleCount = getMSAASamplesFlag(std::min<uint32_t>(mMaxMSAAsupported, mDisplay->cfgR().drawQualityMSAA));
801 mZActive = mZSupported && mDisplay->cfgL().depthBuffer;
802 mMixingSupported = forMixing;
803
804 vk::AttachmentDescription colorAttachment{};
805 colorAttachment.format = mSurfaceFormat.format;
806 colorAttachment.samples = mMSAASampleCount;
807 colorAttachment.loadOp = vk::AttachmentLoadOp::eClear;
808 colorAttachment.storeOp = vk::AttachmentStoreOp::eStore;
809 colorAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
810 colorAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
811 colorAttachment.initialLayout = vk::ImageLayout::eUndefined;
812 colorAttachment.finalLayout = (mMSAASampleCount != vk::SampleCountFlagBits::e1 || mDownsampleFSAA) ? vk::ImageLayout::eColorAttachmentOptimal : vk::ImageLayout::ePresentSrcKHR;
813 vk::AttachmentDescription depthAttachment{};
814 depthAttachment.format = vk::Format::eD32Sfloat;
815 depthAttachment.samples = mMSAASampleCount;
816 depthAttachment.loadOp = vk::AttachmentLoadOp::eClear;
817 depthAttachment.storeOp = vk::AttachmentStoreOp::eDontCare;
818 depthAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
819 depthAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
820 depthAttachment.initialLayout = vk::ImageLayout::eUndefined;
821 depthAttachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
822 vk::AttachmentDescription colorAttachmentResolve{};
823 colorAttachmentResolve.format = mSurfaceFormat.format;
824 colorAttachmentResolve.samples = vk::SampleCountFlagBits::e1;
825 colorAttachmentResolve.loadOp = vk::AttachmentLoadOp::eDontCare;
826 colorAttachmentResolve.storeOp = vk::AttachmentStoreOp::eStore;
827 colorAttachmentResolve.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
828 colorAttachmentResolve.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
829 colorAttachmentResolve.initialLayout = vk::ImageLayout::eUndefined;
830 colorAttachmentResolve.finalLayout = mDownsampleFSAA ? vk::ImageLayout::eColorAttachmentOptimal : vk::ImageLayout::ePresentSrcKHR;
831 int32_t nAttachments = 0;
832 vk::AttachmentReference colorAttachmentRef{};
833 colorAttachmentRef.attachment = nAttachments++;
834 colorAttachmentRef.layout = vk::ImageLayout::eColorAttachmentOptimal;
835 vk::AttachmentReference depthAttachmentRef{};
836 // depthAttachmentRef.attachment // below
837 depthAttachmentRef.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
838 vk::AttachmentReference colorAttachmentResolveRef{};
839 // colorAttachmentResolveRef.attachment // below
840 colorAttachmentResolveRef.layout = vk::ImageLayout::eColorAttachmentOptimal;
841 vk::SubpassDescription subpass{};
842 subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
843 subpass.colorAttachmentCount = 1;
844 subpass.pColorAttachments = &colorAttachmentRef;
845 vk::SubpassDependency dependency{};
846 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
847 dependency.dstSubpass = 0;
848 dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
849 dependency.srcAccessMask = {};
850 dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
851 dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
852
853 std::vector<vk::AttachmentDescription> attachments = {colorAttachment};
854 if (mZActive) {
855 attachments.emplace_back(depthAttachment);
856 depthAttachmentRef.attachment = nAttachments++;
857 subpass.pDepthStencilAttachment = &depthAttachmentRef;
858 }
859 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) {
860 attachments.emplace_back(colorAttachmentResolve);
861 colorAttachmentResolveRef.attachment = nAttachments++;
862 subpass.pResolveAttachments = &colorAttachmentResolveRef;
863 }
864
865 vk::RenderPassCreateInfo renderPassInfo{};
866 renderPassInfo.attachmentCount = attachments.size();
867 renderPassInfo.pAttachments = attachments.data();
868 renderPassInfo.subpassCount = 1;
869 renderPassInfo.pSubpasses = &subpass;
870 renderPassInfo.dependencyCount = 1;
871 renderPassInfo.pDependencies = &dependency;
872 mRenderPass = mDevice.createRenderPass(renderPassInfo, nullptr);
873
874 const uint32_t imageCountWithMixImages = mImageCount * (mMixingSupported ? 2 : 1);
875 mRenderTargetView.resize(imageCountWithMixImages);
876 mFramebuffers.resize(imageCountWithMixImages);
877 if (mDownsampleFSAA) {
878 mDownsampleImages.resize(imageCountWithMixImages);
879 }
880 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) {
881 mMSAAImages.resize(imageCountWithMixImages);
882 }
883 if (mZActive) {
884 mZImages.resize(imageCountWithMixImages);
885 }
886 if (mMSAASampleCount != vk::SampleCountFlagBits::e1 || mZActive || mDownsampleFSAA) {
888 }
889 if (mMixingSupported) {
890 if (mMSAASampleCount != vk::SampleCountFlagBits::e1 || mZActive || mDownsampleFSAA) {
892 }
893 if (!mDownsampleFSAA) {
894 mMixImages.resize(mImageCount);
895 }
896 }
897
898 // Text overlay goes as extra rendering path
899 renderPassInfo.attachmentCount = 1; // Remove depth and MSAA attachments
900 renderPassInfo.pAttachments = &colorAttachment;
901 subpass.pDepthStencilAttachment = nullptr;
902 subpass.pResolveAttachments = nullptr;
903 if (mFramebuffersText.size()) {
904 dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; // Remove early fragment test
905 dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
906 dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; // Remove depth/stencil dependencies
907 }
908 colorAttachment.loadOp = vk::AttachmentLoadOp::eLoad; // Don't clear the frame buffer
909 colorAttachment.initialLayout = vk::ImageLayout::ePresentSrcKHR; // Initial layout is not undefined after 1st pass
910 colorAttachment.samples = vk::SampleCountFlagBits::e1; // No MSAA for Text
911 colorAttachment.finalLayout = vk::ImageLayout::ePresentSrcKHR; // Might have been overwritten above for 1st pass in case of MSAA
912 mRenderPassText = mDevice.createRenderPass(renderPassInfo, nullptr);
913
914 if (mMixingSupported) {
915 if (mDownsampleFSAA) {
916 colorAttachment.initialLayout = vk::ImageLayout::eColorAttachmentOptimal;
917 colorAttachment.finalLayout = mDownsampleFSAA ? vk::ImageLayout::eColorAttachmentOptimal : vk::ImageLayout::ePresentSrcKHR;
918 }
919 if (mFramebuffersTexture.size()) {
920 dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; // Remove early fragment test
921 dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
922 dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; // Remove depth/stencil dependencies
923 }
924 mRenderPassTexture = mDevice.createRenderPass(renderPassInfo, nullptr);
925 }
926
927 for (uint32_t i = 0; i < imageCountWithMixImages; i++) {
928 if (i < mImageCount) { // Main render chain
929 // primary buffer mSwapChainImageViews[i] created as part of createSwapChain, not here
930 } else if (!mDownsampleFSAA) { // for rendering to mixBuffer
931 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);
932 mMixImages[i - mImageCount].view = createImageViewI(mDevice, mMixImages[i - mImageCount].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
933 }
934 std::vector<vk::ImageView> att;
935 if (mDownsampleFSAA) {
936 vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment | (i >= mImageCount ? vk::ImageUsageFlagBits::eSampled : vk::ImageUsageFlagBits::eTransferSrc);
937 createImageI(mDevice, mPhysicalDevice, mDownsampleImages[i].image, mDownsampleImages[i].memory, mRenderWidth, mRenderHeight, mSurfaceFormat.format, usage, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
938 mDownsampleImages[i].view = createImageViewI(mDevice, mDownsampleImages[i].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
940 } else {
942 }
943 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) { // First attachment is the render target, either the MSAA buffer or the framebuffer
944 createImageI(mDevice, mPhysicalDevice, mMSAAImages[i].image, mMSAAImages[i].memory, mRenderWidth, mRenderHeight, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal, mMSAASampleCount);
945 mMSAAImages[i].view = createImageViewI(mDevice, mMSAAImages[i].image, mSurfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1);
946 att.emplace_back(mMSAAImages[i].view);
947 } else {
948 att.emplace_back(*mRenderTargetView[i]);
949 }
950 if (mZActive) {
951 createImageI(mDevice, mPhysicalDevice, mZImages[i].image, mZImages[i].memory, mRenderWidth, mRenderHeight, vk::Format::eD32Sfloat, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal, mMSAASampleCount);
952 mZImages[i].view = createImageViewI(mDevice, mZImages[i].image, vk::Format::eD32Sfloat, vk::ImageAspectFlagBits::eDepth, 1);
953 att.emplace_back(mZImages[i].view);
954 }
955 if (mMSAASampleCount != vk::SampleCountFlagBits::e1) { // If we use MSAA, we have to resolve to the framebuffer as the last target
956 att.emplace_back(*mRenderTargetView[i]);
957 }
958
959 vk::FramebufferCreateInfo framebufferInfo{};
960 framebufferInfo.renderPass = mRenderPass;
961 framebufferInfo.attachmentCount = att.size();
962 framebufferInfo.pAttachments = att.data();
963 framebufferInfo.width = mRenderWidth;
964 framebufferInfo.height = mRenderHeight;
965 framebufferInfo.layers = 1;
966 mFramebuffers[i] = mDevice.createFramebuffer(framebufferInfo, nullptr);
967
968 if (i < mImageCount && mFramebuffersText.size()) {
969 framebufferInfo.attachmentCount = 1;
970 framebufferInfo.pAttachments = &mSwapChainImageViews[i];
971 framebufferInfo.renderPass = mRenderPassText;
972 framebufferInfo.width = mScreenWidth;
973 framebufferInfo.height = mScreenHeight;
974 mFramebuffersText[i] = mDevice.createFramebuffer(framebufferInfo, nullptr);
975 }
976
977 if (i >= mImageCount && mFramebuffersTexture.size()) {
978 framebufferInfo.attachmentCount = 1;
979 framebufferInfo.pAttachments = mRenderTargetView[i - mImageCount];
980 framebufferInfo.renderPass = mRenderPassTexture;
981 framebufferInfo.width = mRenderWidth;
982 framebufferInfo.height = mRenderHeight;
983 mFramebuffersTexture[i - mImageCount] = mDevice.createFramebuffer(framebufferInfo, nullptr);
984 }
985 }
986
987 if (mMixingSupported) {
988 float vertices[6][4] = {
989 {0, (float)mRenderHeight, 0.0f, 1.0f},
990 {0, 0, 0.0f, 0.0f},
991 {(float)mRenderWidth, 0, 1.0f, 0.0f},
992 {0, (float)mRenderHeight, 0.0f, 1.0f},
993 {(float)mRenderWidth, 0, 1.0f, 0.0f},
994 {(float)mRenderWidth, (float)mRenderHeight, 1.0f, 1.0f}};
995 mMixingTextureVertexArray = createBuffer(sizeof(vertices), &vertices[0][0], vk::BufferUsageFlagBits::eVertexBuffer, 1);
996
998 for (uint32_t i = 0; i < mFramesInFlight; i++) {
1000 }
1001 }
1002 }
1003
1005}
1006
1008{
1009 clearVector(mFramebuffers, [&](auto& x) { mDevice.destroyFramebuffer(x, nullptr); });
1010 clearVector(mMSAAImages, [&](auto& x) { clearImage(x); });
1011 clearVector(mDownsampleImages, [&](auto& x) { clearImage(x); });
1012 clearVector(mZImages, [&](auto& x) { clearImage(x); });
1013 clearVector(mMixImages, [&](auto& x) { clearImage(x); });
1014 clearVector(mFramebuffersText, [&](auto& x) { mDevice.destroyFramebuffer(x, nullptr); });
1015 clearVector(mFramebuffersTexture, [&](auto& x) { mDevice.destroyFramebuffer(x, nullptr); });
1016 mDevice.destroyRenderPass(mRenderPass, nullptr);
1017 mDevice.destroyRenderPass(mRenderPassText, nullptr);
1018 if (mMixingSupported) {
1019 mDevice.destroyRenderPass(mRenderPassTexture, nullptr);
1021 }
1022}
1023
1024// ---------------------------- VULKAN PIPELINE ----------------------------
1025
1027{
1028 vk::PipelineShaderStageCreateInfo shaderStages[2] = {vk::PipelineShaderStageCreateInfo{}, vk::PipelineShaderStageCreateInfo{}};
1029 vk::PipelineShaderStageCreateInfo& vertShaderStageInfo = shaderStages[0];
1030 vertShaderStageInfo.stage = vk::ShaderStageFlagBits::eVertex;
1031 // vertShaderStageInfo.module // below
1032 vertShaderStageInfo.pName = "main";
1033 vk::PipelineShaderStageCreateInfo& fragShaderStageInfo = shaderStages[1];
1034 fragShaderStageInfo.stage = vk::ShaderStageFlagBits::eFragment;
1035 // fragShaderStageInfo.module // below
1036 fragShaderStageInfo.pName = "main";
1037
1038 vk::VertexInputBindingDescription bindingDescription{};
1039 bindingDescription.binding = 0;
1040 // bindingDescription.stride // below
1041 bindingDescription.inputRate = vk::VertexInputRate::eVertex;
1042
1043 vk::VertexInputAttributeDescription attributeDescriptions{};
1044 attributeDescriptions.binding = 0;
1045 attributeDescriptions.location = 0;
1046 // attributeDescriptions.format // below
1047 attributeDescriptions.offset = 0;
1048
1049 vk::PipelineVertexInputStateCreateInfo vertexInputInfo{};
1050 vertexInputInfo.vertexBindingDescriptionCount = 1;
1051 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1052 vertexInputInfo.vertexAttributeDescriptionCount = 1;
1053 vertexInputInfo.pVertexAttributeDescriptions = &attributeDescriptions;
1054 vk::PipelineInputAssemblyStateCreateInfo inputAssembly{};
1055 // inputAssembly.topology // below
1056 inputAssembly.primitiveRestartEnable = false;
1057
1058 vk::Viewport viewport{};
1059 viewport.x = 0.0f;
1060 viewport.y = 0.0f;
1061 // viewport.width // below
1062 // viewport.height // below
1063 viewport.minDepth = 0.0f;
1064 viewport.maxDepth = 1.0f;
1065
1066 vk::Rect2D scissor{};
1067 scissor.offset = vk::Offset2D{0, 0};
1068 // scissor.extent // below
1069
1070 vk::PipelineViewportStateCreateInfo viewportState{};
1071 viewportState.viewportCount = 1;
1072 viewportState.pViewports = &viewport;
1073 viewportState.scissorCount = 1;
1074 viewportState.pScissors = &scissor;
1075
1076 vk::PipelineRasterizationStateCreateInfo rasterizer{};
1077 rasterizer.depthClampEnable = false;
1078 rasterizer.rasterizerDiscardEnable = false;
1079 rasterizer.polygonMode = vk::PolygonMode::eFill;
1080 rasterizer.lineWidth = mDisplay->cfgL().lineWidth;
1081 rasterizer.cullMode = vk::CullModeFlagBits::eBack;
1082 rasterizer.frontFace = vk::FrontFace::eClockwise;
1083 rasterizer.depthBiasEnable = false;
1084 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1085 rasterizer.depthBiasClamp = 0.0f; // Optional
1086 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1087
1088 vk::PipelineMultisampleStateCreateInfo multisampling{};
1089 multisampling.sampleShadingEnable = false;
1090 // multisampling.rasterizationSamples // below
1091 multisampling.minSampleShading = 1.0f; // Optional
1092 multisampling.pSampleMask = nullptr; // Optional
1093 multisampling.alphaToCoverageEnable = false; // Optional
1094 multisampling.alphaToOneEnable = false; // Optional
1095
1096 vk::PipelineColorBlendAttachmentState colorBlendAttachment{};
1097 colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
1098 // colorBlendAttachment.blendEnable // below
1099 colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
1100 colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
1101 colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
1102 colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
1103 colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
1104 colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero;
1105 colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd;
1106
1107 vk::PipelineColorBlendStateCreateInfo colorBlending{};
1108 colorBlending.logicOpEnable = false;
1109 colorBlending.logicOp = vk::LogicOp::eCopy;
1110 colorBlending.attachmentCount = 1;
1111 colorBlending.pAttachments = &colorBlendAttachment;
1112 colorBlending.blendConstants[0] = 0.0f;
1113 colorBlending.blendConstants[1] = 0.0f;
1114 colorBlending.blendConstants[2] = 0.0f;
1115 colorBlending.blendConstants[3] = 0.0f;
1116
1117 vk::PipelineDepthStencilStateCreateInfo depthStencil{};
1118 depthStencil.depthTestEnable = true;
1119 depthStencil.depthWriteEnable = true;
1120 depthStencil.depthCompareOp = vk::CompareOp::eLess;
1121 depthStencil.depthBoundsTestEnable = false;
1122 depthStencil.stencilTestEnable = false;
1123
1124 vk::DynamicState dynamicStates[] = {vk::DynamicState::eLineWidth};
1125 vk::PipelineDynamicStateCreateInfo dynamicState{};
1126 dynamicState.dynamicStateCount = 1;
1127 dynamicState.pDynamicStates = dynamicStates;
1128
1129 vk::PushConstantRange pushConstantRanges[2] = {vk::PushConstantRange{}, vk::PushConstantRange{}};
1130 pushConstantRanges[0].stageFlags = vk::ShaderStageFlagBits::eFragment;
1131 pushConstantRanges[0].offset = 0;
1132 pushConstantRanges[0].size = sizeof(float) * 4;
1133 pushConstantRanges[1].stageFlags = vk::ShaderStageFlagBits::eVertex;
1134 pushConstantRanges[1].offset = pushConstantRanges[0].size;
1135 pushConstantRanges[1].size = sizeof(float);
1136 vk::PipelineLayoutCreateInfo pipelineLayoutInfo{};
1137 pipelineLayoutInfo.setLayoutCount = 1;
1138 pipelineLayoutInfo.pSetLayouts = &mUniformDescriptor;
1139 pipelineLayoutInfo.pushConstantRangeCount = 2;
1140 pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges;
1141 mPipelineLayout = mDevice.createPipelineLayout(pipelineLayoutInfo, nullptr);
1142 pipelineLayoutInfo.setLayoutCount = 1;
1143 pipelineLayoutInfo.pSetLayouts = &mUniformDescriptorTexture;
1144 mPipelineLayoutTexture = mDevice.createPipelineLayout(pipelineLayoutInfo, nullptr);
1145
1146 vk::GraphicsPipelineCreateInfo pipelineInfo{};
1147 pipelineInfo.stageCount = 2;
1148 pipelineInfo.pVertexInputState = &vertexInputInfo;
1149 pipelineInfo.pInputAssemblyState = &inputAssembly;
1150 pipelineInfo.pViewportState = &viewportState;
1151 pipelineInfo.pRasterizationState = &rasterizer;
1152 pipelineInfo.pMultisampleState = &multisampling;
1153 // pipelineInfo.pDepthStencilState // below
1154 pipelineInfo.pColorBlendState = &colorBlending;
1155 pipelineInfo.pDynamicState = &dynamicState;
1156 // pipelineInfo.layout // below
1157 // pipelineInfo.renderPass // below
1158 pipelineInfo.subpass = 0;
1159 pipelineInfo.pStages = shaderStages;
1160 pipelineInfo.basePipelineHandle = VkPipeline(VK_NULL_HANDLE); // Optional
1161 pipelineInfo.basePipelineIndex = -1; // Optional
1162
1163 mPipelines.resize(mMixingSupported ? 5 : 4);
1164 static constexpr vk::PrimitiveTopology types[3] = {vk::PrimitiveTopology::ePointList, vk::PrimitiveTopology::eLineList, vk::PrimitiveTopology::eLineStrip};
1165 for (uint32_t i = 0; i < mPipelines.size(); i++) {
1166 if (i == 4) { // Texture rendering
1167 bindingDescription.stride = 4 * sizeof(float);
1168 attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat;
1169 inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
1170 vertShaderStageInfo.module = mShaders["vertexTexture"];
1171 fragShaderStageInfo.module = mShaders["fragmentTexture"];
1172 pipelineInfo.layout = mPipelineLayoutTexture;
1173 pipelineInfo.renderPass = mRenderPassTexture;
1174 pipelineInfo.pDepthStencilState = nullptr;
1175 colorBlendAttachment.blendEnable = true;
1176 multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
1177 viewport.width = scissor.extent.width = mRenderWidth;
1178 viewport.height = scissor.extent.height = mRenderHeight;
1179 } else if (i == 3) { // Text rendering
1180 bindingDescription.stride = 4 * sizeof(float);
1181 attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat;
1182 inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
1183 vertShaderStageInfo.module = mShaders["vertexTexture"];
1184 fragShaderStageInfo.module = mShaders["fragmentText"];
1185 pipelineInfo.layout = mPipelineLayoutTexture;
1186 pipelineInfo.renderPass = mRenderPassText;
1187 pipelineInfo.pDepthStencilState = nullptr;
1188 colorBlendAttachment.blendEnable = true;
1189 multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
1190 viewport.width = scissor.extent.width = mScreenWidth;
1191 viewport.height = scissor.extent.height = mScreenHeight;
1192 } else { // Point / line / line-strip rendering
1193 bindingDescription.stride = 3 * sizeof(float);
1194 attributeDescriptions.format = vk::Format::eR32G32B32Sfloat;
1195 inputAssembly.topology = types[i];
1196 vertShaderStageInfo.module = mShaders[types[i] == vk::PrimitiveTopology::ePointList ? "vertexPoint" : "vertex"];
1197 fragShaderStageInfo.module = mShaders["fragment"];
1198 pipelineInfo.layout = mPipelineLayout;
1199 pipelineInfo.renderPass = mRenderPass;
1200 pipelineInfo.pDepthStencilState = mZActive ? &depthStencil : nullptr;
1201 colorBlendAttachment.blendEnable = true;
1202 multisampling.rasterizationSamples = mMSAASampleCount;
1203 viewport.width = scissor.extent.width = mRenderWidth;
1204 viewport.height = scissor.extent.height = mRenderHeight;
1205 }
1206
1207 CHKERR(mDevice.createGraphicsPipelines(VkPipelineCache(VK_NULL_HANDLE), 1, &pipelineInfo, nullptr, &mPipelines[i])); // TODO: multiple at once + cache?
1208 }
1209}
1210
1211void GPUDisplayBackendVulkan::startFillCommandBuffer(vk::CommandBuffer& commandBuffer, uint32_t imageIndex, bool toMixBuffer)
1212{
1213 commandBuffer.reset({});
1214
1215 vk::CommandBufferBeginInfo beginInfo{};
1216 beginInfo.flags = {};
1217 commandBuffer.begin(beginInfo);
1218
1219 vk::ClearValue clearValues[2];
1220 clearValues[0].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}};
1221 clearValues[1].depthStencil = vk::ClearDepthStencilValue{{1.0f, 0}};
1222
1223 vk::RenderPassBeginInfo renderPassInfo{};
1224 renderPassInfo.renderPass = mRenderPass;
1225 renderPassInfo.framebuffer = toMixBuffer ? mFramebuffers[imageIndex + mImageCount] : mFramebuffers[imageIndex];
1226 renderPassInfo.renderArea.offset = vk::Offset2D{0, 0};
1227 renderPassInfo.renderArea.extent = vk::Extent2D{mRenderWidth, mRenderHeight};
1228 renderPassInfo.clearValueCount = mZActive ? 2 : 1;
1229 renderPassInfo.pClearValues = clearValues;
1230 commandBuffer.beginRenderPass(&renderPassInfo, vk::SubpassContents::eInline);
1231
1232 vk::DeviceSize offsets[] = {0};
1233 commandBuffer.bindVertexBuffers(0, 1, &mVBO.buffer, offsets);
1234 commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mPipelineLayout, 0, 1, &mDescriptorSets[0][mCurrentBufferSet], 0, nullptr);
1235}
1236
1237void GPUDisplayBackendVulkan::endFillCommandBuffer(vk::CommandBuffer& commandBuffer)
1238{
1239 commandBuffer.endRenderPass();
1240 commandBuffer.end();
1241}
1242
1244{
1245 clearVector(mPipelines, [&](auto& x) { mDevice.destroyPipeline(x, nullptr); });
1246 mDevice.destroyPipelineLayout(mPipelineLayout, nullptr);
1247 mDevice.destroyPipelineLayout(mPipelineLayoutTexture, nullptr);
1248}
1249
1250// ---------------------------- VULKAN SHADERS ----------------------------
1251
1252#define LOAD_SHADER(file, ext) \
1253 mShaders[#file] = createShaderModule(_binary_shaders_shaders_##file##_##ext##_spv_start, _binary_shaders_shaders_##file##_##ext##_spv_len, mDevice)
1254
1256{
1257 LOAD_SHADER(vertex, vert);
1258 LOAD_SHADER(fragment, frag);
1259 LOAD_SHADER(vertexPoint, vert);
1260 LOAD_SHADER(vertexTexture, vert);
1261 LOAD_SHADER(fragmentTexture, frag);
1262 LOAD_SHADER(fragmentText, frag);
1263}
1264
1266{
1267 clearVector(mShaders, [&](auto& x) { mDevice.destroyShaderModule(x.second, nullptr); });
1268}
1269
1270// ---------------------------- VULKAN BUFFERS ----------------------------
1271
1273{
1274 if (buffer.deviceMemory != 1) {
1275 void* dstData;
1276 CHKERR(mDevice.mapMemory(buffer.memory, 0, buffer.size, {}, &dstData));
1277 memcpy(dstData, srcData, size);
1278 mDevice.unmapMemory(buffer.memory);
1279 } else {
1280 auto tmp = createBuffer(size, srcData, vk::BufferUsageFlagBits::eTransferSrc, 0);
1281
1282 vk::CommandBuffer commandBuffer = getSingleTimeCommandBuffer();
1283 vk::BufferCopy copyRegion{};
1284 copyRegion.size = size;
1285 commandBuffer.copyBuffer(tmp.buffer, buffer.buffer, 1, &copyRegion);
1286 submitSingleTimeCommandBuffer(commandBuffer);
1287
1288 clearBuffer(tmp);
1289 }
1290}
1291
1292GPUDisplayBackendVulkan::VulkanBuffer GPUDisplayBackendVulkan::createBuffer(size_t size, const void* srcData, vk::BufferUsageFlags type, int32_t deviceMemory)
1293{
1294 vk::MemoryPropertyFlags properties;
1295 if (deviceMemory) {
1296 properties |= vk::MemoryPropertyFlagBits::eDeviceLocal;
1297 }
1298 if (deviceMemory == 0 || deviceMemory == 2) {
1299 properties |= (vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
1300 }
1301 if (deviceMemory == 1) {
1302 type |= vk::BufferUsageFlagBits::eTransferDst;
1303 }
1304
1306 vk::BufferCreateInfo bufferInfo{};
1307 bufferInfo.size = size;
1308 bufferInfo.usage = type;
1309 bufferInfo.sharingMode = vk::SharingMode::eExclusive;
1310 buffer.buffer = mDevice.createBuffer(bufferInfo, nullptr);
1311
1312 vk::MemoryRequirements memRequirements;
1313 memRequirements = mDevice.getBufferMemoryRequirements(buffer.buffer);
1314 vk::MemoryAllocateInfo allocInfo{};
1315 allocInfo.allocationSize = memRequirements.size;
1316 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties, mPhysicalDevice);
1317 buffer.memory = mDevice.allocateMemory(allocInfo, nullptr);
1318
1319 mDevice.bindBufferMemory(buffer.buffer, buffer.memory, 0);
1320
1321 buffer.size = size;
1322 buffer.deviceMemory = deviceMemory;
1323
1324 if (srcData != nullptr) {
1325 writeToBuffer(buffer, size, srcData);
1326 }
1327
1328 return buffer;
1329}
1330
1332{
1333 mDevice.destroyBuffer(buffer.buffer, nullptr);
1334 mDevice.freeMemory(buffer.memory, nullptr);
1335}
1336
1338{
1339 if (mVBO.size) {
1341 mVBO.size = 0;
1342 }
1346 }
1347 for (auto& buf : mFontVertexBuffer) {
1348 if (buf.size) {
1350 }
1351 buf.size = 0;
1352 }
1353}
1354
1355// ---------------------------- VULKAN TEXTURES ----------------------------
1356
1357void GPUDisplayBackendVulkan::writeToImage(VulkanImage& image, const void* srcData, size_t srcSize)
1358{
1359 auto tmp = createBuffer(srcSize, srcData, vk::BufferUsageFlagBits::eTransferSrc, 0);
1360
1361 vk::CommandBuffer commandBuffer = getSingleTimeCommandBuffer();
1362 cmdImageMemoryBarrier(commandBuffer, image.image, {}, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer);
1363 vk::BufferImageCopy region{};
1364 region.bufferOffset = 0;
1365 region.bufferRowLength = 0;
1366 region.bufferImageHeight = 0;
1367 region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1368 region.imageSubresource.mipLevel = 0;
1369 region.imageSubresource.baseArrayLayer = 0;
1370 region.imageSubresource.layerCount = 1;
1371 region.imageOffset = vk::Offset3D{0, 0, 0};
1372 region.imageExtent = vk::Extent3D{image.sizex, image.sizey, 1};
1373 commandBuffer.copyBufferToImage(tmp.buffer, image.image, vk::ImageLayout::eTransferDstOptimal, 1, &region);
1374 cmdImageMemoryBarrier(commandBuffer, image.image, vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eShaderRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader);
1375 submitSingleTimeCommandBuffer(commandBuffer);
1376
1377 clearBuffer(tmp);
1378}
1379
1380GPUDisplayBackendVulkan::VulkanImage GPUDisplayBackendVulkan::createImage(uint32_t sizex, uint32_t sizey, const void* srcData, size_t srcSize, vk::Format format)
1381{
1383 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);
1384
1385 image.view = createImageViewI(mDevice, image.image, format);
1386
1387 image.sizex = sizex;
1388 image.sizey = sizey;
1389 image.format = format;
1390
1391 if (srcData) {
1392 writeToImage(image, srcData, srcSize);
1393 }
1394 return image;
1395}
1396
1398{
1399 mDevice.destroyImageView(image.view, nullptr);
1400 mDevice.destroyImage(image.image, nullptr);
1401 mDevice.freeMemory(image.memory, nullptr);
1402}
1403
1404// ---------------------------- VULKAN INIT EXIT ----------------------------
1405
1407{
1409 mFramesInFlight = 2;
1410
1411 createDevice();
1412 createShaders();
1417
1418 return (0);
1419}
1420
1441
1442// ---------------------------- USER CODE ----------------------------
1443
1445{
1446 if (mScreenWidth == width && mScreenHeight == height) {
1447 return;
1448 }
1450 vk::Extent2D extent = chooseSwapExtent(mSwapChainDetails.capabilities);
1451 if (extent.width != mScreenWidth || extent.height != mScreenHeight) {
1452 mMustUpdateSwapChain = true;
1453 }
1454}
1455
1457{
1458 mDevice.waitIdle();
1460 mVBO = createBuffer(totalVertizes * sizeof(mDisplay->vertexBuffer()[0][0]), mDisplay->vertexBuffer()[0].data(), vk::BufferUsageFlagBits::eVertexBuffer, 1);
1461 if (mDisplay->cfgR().useGLIndirectDraw) {
1463 mIndirectCommandBuffer = createBuffer(mCmdBuffer.size() * sizeof(mCmdBuffer[0]), mCmdBuffer.data(), vk::BufferUsageFlagBits::eIndirectBuffer, 1);
1464 mCmdBuffer.clear();
1465 }
1467}
1468
1470{
1471 auto first = std::get<0>(v);
1472 auto count = std::get<1>(v);
1473 auto iSector = std::get<2>(v);
1474 if (count == 0) {
1475 return 0;
1476 }
1478 return count;
1479 }
1480
1482 mCurrentCommandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[tt]);
1484 }
1485 if (mDisplay->cfgR().useGLIndirectDraw) {
1487 } else {
1488 for (uint32_t k = 0; k < count; k++) {
1489 mCurrentCommandBuffer.draw(mDisplay->vertexBufferCount()[iSector][first + k], 1, mDisplay->vertexBufferStart()[iSector][first + k], 0);
1490 }
1491 }
1492
1493 return count;
1494}
1495
1496void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
1497{
1498 if (mDisplay->updateDrawCommands() || toMixBuffer || includeMixImage > 0) {
1500 }
1501
1502 if (includeMixImage == 0.f) {
1504 CHKERR(mDevice.waitForFences(1, &mInFlightFence[mCurrentFrame], true, UINT64_MAX));
1505 auto getImage = [&]() {
1506 vk::Fence fen = VkFence(VK_NULL_HANDLE);
1507 vk::Semaphore sem = VkSemaphore(VK_NULL_HANDLE);
1510 CHKERR(mDevice.resetFences(1, &fen));
1511 } else {
1513 }
1514 return mDevice.acquireNextImageKHR(mSwapChain, UINT64_MAX, sem, fen, &mCurrentImageIndex);
1515 };
1516
1517 vk::Result retVal = vk::Result::eSuccess;
1518 bool mustUpdateRendering = mMustUpdateSwapChain;
1519 if (mDisplay->updateRenderPipeline() || (requestScreenshot && !mSwapchainImageReadable) || (toMixBuffer && !mMixingSupported) || mDownsampleFactor != getDownsampleFactor(requestScreenshot)) {
1520 mustUpdateRendering = true;
1521 } else if (!mMustUpdateSwapChain) {
1522 retVal = getImage();
1523 }
1524 if (mMustUpdateSwapChain || mustUpdateRendering || retVal == vk::Result::eErrorOutOfDateKHR || retVal == vk::Result::eSuboptimalKHR) {
1525 if (!mustUpdateRendering) {
1526 GPUInfo("Pipeline out of data / suboptimal, recreating");
1527 }
1528 recreateRendering(requestScreenshot, toMixBuffer);
1529 retVal = getImage();
1530 }
1531 CHKERR(retVal);
1533 CHKERR(mDevice.waitForFences(1, &mInFlightFence[mCurrentFrame], true, UINT64_MAX));
1534 }
1535 CHKERR(mDevice.resetFences(1, &mInFlightFence[mCurrentFrame]));
1536 mMustUpdateSwapChain = false;
1537 mHasDrawnText = false;
1539
1540 const hmm_mat4 modelViewProj = proj * view;
1541 writeToBuffer(mUniformBuffersMat[0][mCurrentBufferSet], sizeof(modelViewProj), &modelViewProj);
1542 }
1543
1548 }
1549}
1550
1551void GPUDisplayBackendVulkan::finishDraw(bool doScreenshot, bool toMixBuffer, float includeMixImage)
1552{
1555 if (!toMixBuffer && includeMixImage == 0.f && mCommandBufferPerImage) {
1557 }
1558 }
1559}
1560
1561void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, float includeMixImage)
1562{
1563 vk::Semaphore* stageFinishedSemaphore = &mRenderFinishedSemaphore[mCurrentFrame];
1564 const vk::Fence noFence = VkFence(VK_NULL_HANDLE);
1565
1566 vk::SubmitInfo submitInfo{};
1567 vk::PipelineStageFlags waitStages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput};
1568 submitInfo.pWaitSemaphores = includeMixImage > 0.f ? &mRenderFinishedSemaphore[mCurrentFrame] : (!mCommandBufferPerImage ? &mImageAvailableSemaphore[mCurrentFrame] : nullptr);
1569 submitInfo.waitSemaphoreCount = submitInfo.pWaitSemaphores != nullptr ? 1 : 0;
1570 submitInfo.pWaitDstStageMask = waitStages;
1571 submitInfo.commandBufferCount = 1;
1572 submitInfo.pCommandBuffers = &mCurrentCommandBuffer;
1573 submitInfo.signalSemaphoreCount = 1;
1574 submitInfo.pSignalSemaphores = stageFinishedSemaphore;
1575 CHKERR(mGraphicsQueue.submit(1, &submitInfo, includeMixImage > 0 || toMixBuffer || mHasDrawnText || mDownsampleFSAA ? noFence : mInFlightFence[mCurrentFrame]));
1576 if (!toMixBuffer) {
1577 if (includeMixImage > 0.f) {
1579 submitInfo.pWaitSemaphores = stageFinishedSemaphore;
1580 waitStages[0] = {vk::PipelineStageFlagBits::eColorAttachmentOutput};
1581 submitInfo.waitSemaphoreCount = 1;
1582 submitInfo.pCommandBuffers = &mCommandBuffersTexture[mCurrentBufferSet];
1583 stageFinishedSemaphore = &mMixFinishedSemaphore[mCurrentFrame];
1584 submitInfo.pSignalSemaphores = stageFinishedSemaphore;
1585 CHKERR(mGraphicsQueue.submit(1, &submitInfo, mHasDrawnText || mDownsampleFSAA ? noFence : mInFlightFence[mCurrentFrame]));
1586 }
1587
1588 if (mDownsampleFSAA) {
1590 submitInfo.pCommandBuffers = &mCommandBuffersDownsample[mCurrentBufferSet];
1591 submitInfo.pWaitSemaphores = stageFinishedSemaphore;
1592 waitStages[0] = {vk::PipelineStageFlagBits::eTransfer};
1593 submitInfo.waitSemaphoreCount = 1;
1594 stageFinishedSemaphore = &mDownsampleFinishedSemaphore[mCurrentFrame];
1595 submitInfo.pSignalSemaphores = stageFinishedSemaphore;
1596 CHKERR(mGraphicsQueue.submit(1, &submitInfo, mHasDrawnText ? noFence : mInFlightFence[mCurrentFrame]));
1597 }
1598
1599 if (doScreenshot) {
1600 mDevice.waitIdle();
1601 if (mDisplay->cfgR().screenshotScaleFactor != 1) {
1602 readImageToPixels(mDownsampleImages[mCurrentImageIndex].image, vk::ImageLayout::eColorAttachmentOptimal, mScreenshotPixels);
1603 } else {
1604 readImageToPixels(mSwapChainImages[mCurrentImageIndex], vk::ImageLayout::ePresentSrcKHR, mScreenshotPixels);
1605 }
1606 }
1607
1608 if (mHasDrawnText) {
1609 submitInfo.pWaitSemaphores = stageFinishedSemaphore;
1610 waitStages[0] = {vk::PipelineStageFlagBits::eColorAttachmentOutput};
1611 submitInfo.waitSemaphoreCount = 1;
1612 submitInfo.pCommandBuffers = &mCommandBuffersText[mCurrentBufferSet];
1613 stageFinishedSemaphore = &mTextFinishedSemaphore[mCurrentFrame];
1614 submitInfo.pSignalSemaphores = stageFinishedSemaphore;
1615 CHKERR(mGraphicsQueue.submit(1, &submitInfo, mInFlightFence[mCurrentFrame]));
1616 }
1617
1618 CHKERR(mDevice.waitForFences(1, &mInFlightFence[mCurrentFrame], true, UINT64_MAX)); // TODO: I think we need to wait for the fence, so that the image was acquired before we present. Perhaps we can present later to avoid delays
1619 vk::PresentInfoKHR presentInfo{};
1620 presentInfo.waitSemaphoreCount = 1;
1621 presentInfo.pWaitSemaphores = stageFinishedSemaphore;
1622 presentInfo.swapchainCount = 1;
1623 presentInfo.pSwapchains = &mSwapChain;
1624 presentInfo.pImageIndices = &mCurrentImageIndex;
1625 presentInfo.pResults = nullptr;
1626 vk::Result retVal = mGraphicsQueue.presentKHR(&presentInfo);
1627 if (retVal == vk::Result::eErrorOutOfDateKHR) {
1628 mMustUpdateSwapChain = true;
1629 } else {
1630 CHKERR(retVal);
1631 }
1632 }
1633}
1634
1635void GPUDisplayBackendVulkan::downsampleToFramebuffer(vk::CommandBuffer& commandBuffer)
1636{
1637 commandBuffer.reset({});
1638 vk::CommandBufferBeginInfo beginInfo{};
1639 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1640 commandBuffer.begin(beginInfo);
1641
1642 cmdImageMemoryBarrier(commandBuffer, mSwapChainImages[mCurrentImageIndex], {}, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1643 cmdImageMemoryBarrier(commandBuffer, mDownsampleImages[mCurrentImageIndex].image, vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferRead, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1644
1645 vk::Offset3D blitSizeSrc;
1646 blitSizeSrc.x = mRenderWidth;
1647 blitSizeSrc.y = mRenderHeight;
1648 blitSizeSrc.z = 1;
1649 vk::Offset3D blitSizeDst;
1650 blitSizeDst.x = mScreenWidth;
1651 blitSizeDst.y = mScreenHeight;
1652 blitSizeDst.z = 1;
1653 vk::ImageBlit imageBlitRegion{};
1654 imageBlitRegion.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1655 imageBlitRegion.srcSubresource.layerCount = 1;
1656 imageBlitRegion.srcOffsets[1] = blitSizeSrc;
1657 imageBlitRegion.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1658 imageBlitRegion.dstSubresource.layerCount = 1;
1659 imageBlitRegion.dstOffsets[1] = blitSizeDst;
1660 commandBuffer.blitImage(mDownsampleImages[mCurrentImageIndex].image, vk::ImageLayout::eTransferSrcOptimal, mSwapChainImages[mCurrentImageIndex], vk::ImageLayout::eTransferDstOptimal, 1, &imageBlitRegion, vk::Filter::eLinear);
1661
1662 cmdImageMemoryBarrier(commandBuffer, mSwapChainImages[mCurrentImageIndex], vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eMemoryRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::ePresentSrcKHR, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1663 cmdImageMemoryBarrier(commandBuffer, mDownsampleImages[mCurrentImageIndex].image, vk::AccessFlagBits::eTransferRead, vk::AccessFlagBits::eMemoryRead, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1664
1665 commandBuffer.end();
1666}
1667
1669{
1670 hmm_mat4 proj = HMM_Orthographic(0.f, mScreenWidth, 0.f, mScreenHeight, -1, 1);
1671 writeToBuffer(mUniformBuffersMat[1][mCurrentBufferSet], sizeof(proj), &proj);
1672
1673 mFontVertexBufferHost.clear();
1674 mTextDrawCommands.clear();
1675}
1676
1678{
1679 if (!mHasDrawnText) {
1680 return;
1681 }
1682
1684
1685 vk::CommandBufferBeginInfo beginInfo{};
1686 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1687 mCommandBuffersText[mCurrentBufferSet].begin(beginInfo);
1688
1689 vk::RenderPassBeginInfo renderPassInfo{};
1690 renderPassInfo.renderPass = mRenderPassText;
1692 renderPassInfo.renderArea.offset = vk::Offset2D{0, 0};
1693 renderPassInfo.renderArea.extent = vk::Extent2D{mScreenWidth, mScreenHeight};
1694 renderPassInfo.clearValueCount = 0;
1695 mCommandBuffersText[mCurrentBufferSet].beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
1696
1699 }
1700 mFontVertexBuffer[mCurrentBufferSet] = createBuffer(mFontVertexBufferHost.size() * sizeof(float), mFontVertexBufferHost.data(), vk::BufferUsageFlagBits::eVertexBuffer, 0);
1701
1702 mCommandBuffersText[mCurrentBufferSet].bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[3]);
1703 mCommandBuffersText[mCurrentBufferSet].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mPipelineLayoutTexture, 0, 1, &mDescriptorSets[1][mCurrentBufferSet], 0, nullptr);
1704 vk::DeviceSize offsets[] = {0};
1706
1707 for (const auto& cmd : mTextDrawCommands) {
1708 mCommandBuffersText[mCurrentBufferSet].pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(cmd.color), cmd.color);
1709 mCommandBuffersText[mCurrentBufferSet].draw(cmd.nVertices, 1, cmd.firstVertex, 0);
1710 }
1711
1712 mFontVertexBufferHost.clear();
1713
1714 mCommandBuffersText[mCurrentBufferSet].endRenderPass();
1716}
1717
1718void GPUDisplayBackendVulkan::mixImages(vk::CommandBuffer commandBuffer, float mixSlaveImage)
1719{
1720 hmm_mat4 proj = HMM_Orthographic(0.f, mRenderWidth, 0.f, mRenderHeight, -1, 1);
1721 writeToBuffer(mUniformBuffersMat[2][mCurrentBufferSet], sizeof(proj), &proj);
1722
1723 commandBuffer.reset({});
1724 vk::CommandBufferBeginInfo beginInfo{};
1725 beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
1726 commandBuffer.begin(beginInfo);
1727
1729 vk::ImageLayout srcLayout = mDownsampleFSAA ? vk::ImageLayout::eColorAttachmentOptimal : vk::ImageLayout::ePresentSrcKHR;
1730 cmdImageMemoryBarrier(commandBuffer, image, {}, vk::AccessFlagBits::eMemoryRead, srcLayout, vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eFragmentShader);
1731
1732 vk::RenderPassBeginInfo renderPassInfo{};
1733 renderPassInfo.renderPass = mRenderPassTexture;
1735 renderPassInfo.renderArea.offset = vk::Offset2D{0, 0};
1736 renderPassInfo.renderArea.extent = vk::Extent2D{mRenderWidth, mRenderHeight};
1737 renderPassInfo.clearValueCount = 0;
1738 commandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
1739
1740 commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[4]);
1743 }
1744 commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mPipelineLayoutTexture, 0, 1, &mDescriptorSets[2][mCurrentBufferSet], 0, nullptr);
1745 vk::DeviceSize offsets[] = {0};
1746 commandBuffer.bindVertexBuffers(0, 1, &mMixingTextureVertexArray.buffer, offsets);
1747
1748 commandBuffer.pushConstants(mPipelineLayoutTexture, vk::ShaderStageFlagBits::eFragment, 0, sizeof(mixSlaveImage), &mixSlaveImage);
1749 commandBuffer.draw(6, 1, 0, 0);
1750
1751 commandBuffer.endRenderPass();
1752 commandBuffer.end();
1753}
1754
1756{
1758 return;
1759 }
1760 mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(color), color.data());
1761}
1762
1764{
1766 return;
1767 }
1768 float size = mDisplay->cfgL().pointSize * mDownsampleFactor * factor;
1769 mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, sizeof(std::array<float, 4>), sizeof(size), &size);
1770}
1771
1773{
1775 return;
1776 }
1777 mCurrentCommandBuffer.setLineWidth(mDisplay->cfgL().lineWidth * mDownsampleFactor * factor);
1778}
1779
1784
1785void GPUDisplayBackendVulkan::addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void* data)
1786{
1787 if (symbol != (int32_t)mFontSymbols.size()) {
1788 throw std::runtime_error("Incorrect symbol ID");
1789 }
1790 mFontSymbols.emplace_back(FontSymbolVulkan{{{sizex, sizey}, {offsetx, offsety}, advance}, nullptr, 0.f, 0.f, 0.f, 0.f});
1791 auto& buffer = mFontSymbols.back().data;
1792 if (sizex && sizey) {
1793 buffer.reset(new char[sizex * sizey]);
1794 memcpy(buffer.get(), data, sizex * sizey);
1795 }
1796}
1797
1799{
1800 int32_t maxSizeX = 0, maxSizeY = 0, maxBigX = 0, maxBigY = 0, maxRowY = 0;
1801 bool smooth = smoothFont();
1802 // Build a mega texture containing all fonts
1803 for (auto& symbol : mFontSymbols) {
1804 maxSizeX = std::max(maxSizeX, symbol.size[0]);
1805 maxSizeY = std::max(maxSizeY, symbol.size[1]);
1806 }
1807 uint32_t nn = ceil(std::sqrt(mFontSymbols.size()));
1808 int32_t sizex = nn * maxSizeX;
1809 int32_t sizey = nn * maxSizeY;
1810 std::unique_ptr<char[]> bigImage{new char[sizex * sizey]};
1811 memset(bigImage.get(), 0, sizex * sizey);
1812 int32_t rowy = 0, colx = 0;
1813 for (uint32_t i = 0; i < mFontSymbols.size(); i++) {
1814 auto& s = mFontSymbols[i];
1815 if (colx + s.size[0] > sizex) {
1816 colx = 0;
1817 rowy += maxRowY;
1818 maxRowY = 0;
1819 }
1820 for (int32_t k = 0; k < s.size[1]; k++) {
1821 for (int32_t j = 0; j < s.size[0]; j++) {
1822 int8_t val = s.data.get()[j + k * s.size[0]];
1823 if (!smooth) {
1824 val = val < 0 ? 0xFF : 0;
1825 }
1826 bigImage.get()[(colx + j) + (rowy + k) * sizex] = val;
1827 }
1828 }
1829 s.data.reset();
1830 s.x0 = colx;
1831 s.x1 = colx + s.size[0];
1832 s.y0 = rowy;
1833 s.y1 = rowy + s.size[1];
1834 maxBigX = std::max(maxBigX, colx + s.size[0]);
1835 maxBigY = std::max(maxBigY, rowy + s.size[1]);
1836 maxRowY = std::max(maxRowY, s.size[1]);
1837 colx += s.size[0];
1838 }
1839 if (maxBigX != sizex) {
1840 for (int32_t y = 1; y < maxBigY; y++) {
1841 memmove(bigImage.get() + y * maxBigX, bigImage.get() + y * sizex, maxBigX);
1842 }
1843 }
1844 sizex = maxBigX;
1845 sizey = maxBigY;
1846 for (uint32_t i = 0; i < mFontSymbols.size(); i++) {
1847 auto& s = mFontSymbols[i];
1848 s.x0 /= sizex;
1849 s.x1 /= sizex;
1850 s.y0 /= sizey;
1851 s.y1 /= sizey;
1852 }
1853
1854 mFontImage = createImage(sizex, sizey, bigImage.get(), sizex * sizey, vk::Format::eR8Unorm);
1856}
1857
1859{
1860 vk::DescriptorImageInfo imageInfo{};
1861 imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
1862 imageInfo.imageView = mFontImage.view;
1863 imageInfo.sampler = mTextureSampler;
1864 for (uint32_t i = 0; i < mFramesInFlight; i++) {
1865 vk::WriteDescriptorSet descriptorWrite{};
1866 descriptorWrite.dstSet = mDescriptorSets[1][i];
1867 descriptorWrite.dstBinding = 2;
1868 descriptorWrite.dstArrayElement = 0;
1869 descriptorWrite.descriptorType = vk::DescriptorType::eCombinedImageSampler;
1870 descriptorWrite.descriptorCount = 1;
1871 descriptorWrite.pImageInfo = &imageInfo;
1872 mDevice.updateDescriptorSets(1, &descriptorWrite, 0, nullptr);
1873 }
1874}
1875
1876void GPUDisplayBackendVulkan::OpenGLPrint(const char* s, float x, float y, float* color, float scale)
1877{
1879 return;
1880 }
1881
1882 size_t firstVertex = mFontVertexBufferHost.size() / 4;
1883 if (smoothFont()) {
1884 scale *= 0.25f; // Font size is 48 to have nice bitmap, scale to size 12
1885 }
1886
1887 for (const char* c = s; *c; c++) {
1888 if ((int32_t)*c > (int32_t)mFontSymbols.size()) {
1889 GPUError("Trying to draw unsupported symbol: %d > %d\n", (int32_t)*c, (int32_t)mFontSymbols.size());
1890 continue;
1891 }
1892 const FontSymbolVulkan& sym = mFontSymbols[*c];
1893 if (sym.size[0] && sym.size[1]) {
1894 mHasDrawnText = true;
1895 float xpos = x + sym.offset[0] * scale;
1896 float ypos = y - (sym.size[1] - sym.offset[1]) * scale;
1897 float w = sym.size[0] * scale;
1898 float h = sym.size[1] * scale;
1899 float vertices[6][4] = {
1900 {xpos, mScreenHeight - 1 - ypos, sym.x0, sym.y1},
1901 {xpos, mScreenHeight - 1 - (ypos + h), sym.x0, sym.y0},
1902 {xpos + w, mScreenHeight - 1 - ypos, sym.x1, sym.y1},
1903 {xpos + w, mScreenHeight - 1 - ypos, sym.x1, sym.y1},
1904 {xpos, mScreenHeight - 1 - (ypos + h), sym.x0, sym.y0},
1905 {xpos + w, mScreenHeight - 1 - (ypos + h), sym.x1, sym.y0}};
1906 size_t oldSize = mFontVertexBufferHost.size();
1907 mFontVertexBufferHost.resize(oldSize + 4 * 6);
1908 memcpy(&mFontVertexBufferHost[oldSize], &vertices[0][0], sizeof(vertices));
1909 }
1910 x += (sym.advance >> 6) * scale; // shift is in 1/64th of a pixel
1911 }
1912
1913 size_t nVertices = mFontVertexBufferHost.size() / 4 - firstVertex;
1914
1915 if (nVertices) {
1916 auto& c = mTextDrawCommands;
1917 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]) {
1918 c.back().nVertices += nVertices;
1919 } else {
1920 c.emplace_back(TextDrawCommand{firstVertex, nVertices, {color[0], color[1], color[2], color[3]}});
1921 }
1922 }
1923}
1924
1925void GPUDisplayBackendVulkan::readImageToPixels(vk::Image image, vk::ImageLayout layout, std::vector<char>& pixels)
1926{
1927 uint32_t width = mScreenWidth * mDisplay->cfgR().screenshotScaleFactor;
1928 uint32_t height = mScreenHeight * mDisplay->cfgR().screenshotScaleFactor;
1929 static constexpr int32_t bytesPerPixel = 4;
1930 pixels.resize(width * height * bytesPerPixel);
1931
1932 vk::Image dstImage, dstImage2, src2;
1933 vk::DeviceMemory dstImageMemory, dstImageMemory2;
1934 createImageI(mDevice, mPhysicalDevice, dstImage, dstImageMemory, width, height, vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, vk::ImageTiling::eLinear);
1935 vk::CommandBuffer cmdBuffer = getSingleTimeCommandBuffer();
1936 cmdImageMemoryBarrier(cmdBuffer, image, vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferRead, layout, vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1937 if (mDisplay->cfgR().screenshotScaleFactor != 1) {
1938 createImageI(mDevice, mPhysicalDevice, dstImage2, dstImageMemory2, width, height, mSurfaceFormat.format, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageTiling::eOptimal);
1939 cmdImageMemoryBarrier(cmdBuffer, dstImage2, {}, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1940 vk::Offset3D blitSizeSrc = {(int32_t)mRenderWidth, (int32_t)mRenderHeight, 1};
1941 vk::Offset3D blitSizeDst = {(int32_t)width, (int32_t)height, 1};
1942 vk::ImageBlit imageBlitRegion{};
1943 imageBlitRegion.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1944 imageBlitRegion.srcSubresource.layerCount = 1;
1945 imageBlitRegion.srcOffsets[1] = blitSizeSrc;
1946 imageBlitRegion.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1947 imageBlitRegion.dstSubresource.layerCount = 1;
1948 imageBlitRegion.dstOffsets[1] = blitSizeDst;
1949 cmdBuffer.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, dstImage2, vk::ImageLayout::eTransferDstOptimal, 1, &imageBlitRegion, vk::Filter::eLinear);
1950 src2 = dstImage2;
1951 cmdImageMemoryBarrier(cmdBuffer, dstImage2, vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1952 } else {
1953 src2 = image;
1954 }
1955
1956 cmdImageMemoryBarrier(cmdBuffer, dstImage, {}, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1957 vk::ImageCopy imageCopyRegion{};
1958 imageCopyRegion.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1959 imageCopyRegion.srcSubresource.layerCount = 1;
1960 imageCopyRegion.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
1961 imageCopyRegion.dstSubresource.layerCount = 1;
1962 imageCopyRegion.extent.width = width;
1963 imageCopyRegion.extent.height = height;
1964 imageCopyRegion.extent.depth = 1;
1965 cmdBuffer.copyImage(src2, vk::ImageLayout::eTransferSrcOptimal, dstImage, vk::ImageLayout::eTransferDstOptimal, 1, &imageCopyRegion);
1966
1967 cmdImageMemoryBarrier(cmdBuffer, dstImage, vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eMemoryRead, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eGeneral, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1968 cmdImageMemoryBarrier(cmdBuffer, image, vk::AccessFlagBits::eTransferRead, vk::AccessFlagBits::eMemoryRead, vk::ImageLayout::eTransferSrcOptimal, layout, vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer);
1970
1971 vk::ImageSubresource subResource{vk::ImageAspectFlagBits::eColor, 0, 0};
1972 vk::SubresourceLayout subResourceLayout = mDevice.getImageSubresourceLayout(dstImage, subResource);
1973 const char* data;
1974 CHKERR(mDevice.mapMemory(dstImageMemory, 0, VK_WHOLE_SIZE, {}, (void**)&data));
1975 data += subResourceLayout.offset;
1976 for (uint32_t i = 0; i < height; i++) {
1977 memcpy(pixels.data() + i * width * bytesPerPixel, data + (height - i - 1) * width * bytesPerPixel, width * bytesPerPixel);
1978 }
1979 mDevice.unmapMemory(dstImageMemory);
1980 mDevice.freeMemory(dstImageMemory, nullptr);
1981 mDevice.destroyImage(dstImage, nullptr);
1982 if (mDisplay->cfgR().screenshotScaleFactor != 1) {
1983 mDevice.freeMemory(dstImageMemory2, nullptr);
1984 mDevice.destroyImage(dstImage2, nullptr);
1985 }
1986}
1987
1989{
1990 return 32;
1991}
1992
uint64_t vertex
Definition RawEventData.h:9
int32_t i
#define CHKERR(cmd)
#define LOAD_SHADER(file, ext)
int32_t retVal
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
void setMixDescriptor(int32_t descriptorIndex, int32_t imageIndex)
std::vector< vk::Semaphore > mRenderFinishedSemaphore
void prepareDraw(const hmm_mat4 &proj, const hmm_mat4 &view, bool requestScreenshot, bool toMixBuffer, float includeMixImage) override
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
void pointSizeFactor(float factor) override
std::vector< FontSymbolVulkan > mFontSymbols
std::vector< vk::ImageView * > mRenderTargetView
std::vector< vk::Framebuffer > mFramebuffersTexture
void addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void *data) override
std::vector< vk::Framebuffer > mFramebuffersText
void mixImages(vk::CommandBuffer cmdBuffer, float mixSlaveImage)
void recreateRendering(bool forScreenshot=false, bool forMixing=false)
std::vector< VulkanBuffer > mFontVertexBuffer
void endFillCommandBuffer(vk::CommandBuffer &commandBuffer)
uint32_t drawVertices(const vboList &v, const drawType t) override
std::vector< vk::CommandBuffer > mCommandBuffersDownsample
void ActivateColor(std::array< float, 4 > &color) override
std::vector< vk::Semaphore > mMixFinishedSemaphore
std::vector< vk::Semaphore > mTextFinishedSemaphore
std::vector< VulkanBuffer > mUniformBuffersMat[3]
double checkDevice(vk::PhysicalDevice device, const std::vector< const char * > &reqDeviceExtensions)
void updateSwapChainDetails(const vk::PhysicalDevice &device)
void finishDraw(bool doScreenshot, bool toMixBuffer, float includeMixImage) override
void writeToImage(VulkanImage &image, const void *srcData, size_t srcSize)
std::vector< vk::DescriptorSet > mDescriptorSets[3]
vk::DescriptorSetLayout mUniformDescriptorTexture
void downsampleToFramebuffer(vk::CommandBuffer &commandBuffer)
vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR &capabilities)
std::vector< vk::Framebuffer > mFramebuffers
void readImageToPixels(vk::Image image, vk::ImageLayout layout, std::vector< char > &pixels)
VulkanImage createImage(uint32_t sizex, uint32_t sizey, const void *srcData=nullptr, size_t srcSize=0, vk::Format format=vk::Format::eR8G8B8A8Srgb)
std::vector< vk::Semaphore > mDownsampleFinishedSemaphore
std::vector< TextDrawCommand > mTextDrawCommands
std::unordered_map< std::string, vk::ShaderModule > mShaders
void finishFrame(bool doScreenshot, bool toMixBuffer, float includeMixImage) override
void createSwapChain(bool forScreenshot=false, bool forMixing=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
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
std::vector< vk::CommandBuffer > mCommandBuffersText
void loadDataToGPU(size_t totalVertizes) override
std::vector< VulkanBuffer > mUniformBuffersCol[3]
vecpod< DrawArraysIndirectCommand > mCmdBuffer
std::vector< int32_t > mIndirectSectorOffset
std::vector< char > mScreenshotPixels
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:60
const GPUParam * param()
Definition GPUDisplay.h:74
int32_t updateRenderPipeline() const
Definition GPUDisplay.h:65
const GPUSettingsDisplayRenderer & cfgR() const
Definition GPUDisplay.h:59
vecpod< vtx > * vertexBuffer()
Definition GPUDisplay.h:73
bool drawTextInCompatMode() const
Definition GPUDisplay.h:77
int32_t updateDrawCommands() const
Definition GPUDisplay.h:64
GPUDisplayFrontend * frontend()
Definition GPUDisplay.h:76
const vecpod< uint32_t > * vertexBufferCount() const
Definition GPUDisplay.h:68
const GPUSettingsDisplay & cfg() const
Definition GPUDisplay.h:62
vecpod< int32_t > * vertexBufferStart()
Definition GPUDisplay.h:67
const GPUSettingsProcessing & GetProcessingSettings() const
Definition GPUDisplay.h:75
GLeglImageOES image
Definition glcorearb.h:4021
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum func
Definition glcorearb.h:778
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 GLsizei width
Definition glcorearb.h:270
GLenum GLint * range
Definition glcorearb.h:1899
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean * data
Definition glcorearb.h:298
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
Definition glcorearb.h:275
GLuint GLfloat * val
Definition glcorearb.h:1582
GLsizei const GLenum * attachments
Definition glcorearb.h:2492
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
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
#define QGET_LD_BINARY_SYMBOLS(filename)