Loading...
Searching...
No Matches
VulkanVideoDevice.hpp
1#pragma once
2#include <score/gfx/Vulkan.hpp>
3
4#if QT_HAS_VULKAN && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
5
6#include <QtGui/private/qrhivulkan_p.h>
7#include <qvulkanfunctions.h>
8#include <vulkan/vulkan.h>
9
10#include <cstring>
11#include <string>
12#include <vector>
13
14#ifdef VK_KHR_video_decode_queue
15
16namespace score::gfx
17{
18
27struct SharedVulkanDevice
28{
29 VkPhysicalDevice physDev{VK_NULL_HANDLE};
30 VkDevice dev{VK_NULL_HANDLE};
31 VkQueue gfxQueue{VK_NULL_HANDLE};
32 uint32_t gfxQueueFamilyIdx{0};
33 bool hasVideoDecodeQueue{false};
34 uint32_t videoDecodeQueueFamilyIdx{0};
35
36 // Persistent storage for extension name strings (FFmpeg needs const char*)
37 std::vector<std::string> enabledExtensions;
38
39 // Queue family info for FFmpeg's AVVulkanDeviceContext
40 struct QueueFamilyInfo
41 {
42 uint32_t idx;
43 uint32_t count;
44 VkQueueFlags flags;
45 };
46 std::vector<QueueFamilyInfo> queueFamilies;
47
48 void destroy()
49 {
50 if(dev != VK_NULL_HANDLE)
51 {
52 auto* inst = staticVulkanInstance(false);
53 if(inst)
54 {
55 auto fn = reinterpret_cast<PFN_vkDestroyDevice>(
56 inst->getInstanceProcAddr("vkDestroyDevice"));
57 if(fn)
58 fn(dev, nullptr);
59 }
60 dev = VK_NULL_HANDLE;
61 }
62 }
63
64 explicit operator bool() const { return dev != VK_NULL_HANDLE; }
65};
66
74inline std::vector<const char*> sharedVulkanDeviceExtensions()
75{
76 return {
77 // Qt QRhi requirements
78 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
79#ifdef VK_KHR_MAINTENANCE_1_EXTENSION_NAME
80 VK_KHR_MAINTENANCE_1_EXTENSION_NAME,
81#endif
82#ifdef VK_KHR_MAINTENANCE_2_EXTENSION_NAME
83 VK_KHR_MAINTENANCE_2_EXTENSION_NAME,
84#endif
85#ifdef VK_KHR_MAINTENANCE_3_EXTENSION_NAME
86 VK_KHR_MAINTENANCE_3_EXTENSION_NAME,
87#endif
88#ifdef VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME
89 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
90#endif
91#ifdef VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME
92 VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,
93#endif
94#ifdef VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME
95 VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME,
96#endif
97#ifdef VK_KHR_MULTIVIEW_EXTENSION_NAME
98 VK_KHR_MULTIVIEW_EXTENSION_NAME,
99#endif
100#ifdef VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
101 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
102#endif
103#ifdef VK_KHR_SPIRV_1_4_EXTENSION_NAME
104 VK_KHR_SPIRV_1_4_EXTENSION_NAME,
105#endif
106#ifdef VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME
107 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
108#endif
109 // External memory (DMA-BUF, CUDA interop)
110 VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
111 VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
112#ifdef VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME
113 VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
114#endif
115#ifdef VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME
116 VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
117#endif
118#ifdef VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME
119 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
120#endif
121#ifdef VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME
122 VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
123#endif
124#ifdef VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME
125 VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
126#endif
127#ifdef VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME
128 VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
129#endif
130#ifdef VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME
131 VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
132#endif
133 VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
134 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
135 // Vulkan Video decode
136 VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
137 VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
138#ifdef VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME
139 VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
140#endif
141#ifdef VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME
142 VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
143#endif
144#ifdef VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME
145 VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME,
146#endif
147 // Synchronization
148 VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
149 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
150 // YCbCr
151#ifdef VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME
152 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
153#endif
154#ifdef VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME
155 VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
156#endif
157#ifdef VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME
158 VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
159#endif
160#ifdef VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME
161 VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME,
162#endif
163#ifdef VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME
164 VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME,
165#endif
166#ifdef VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME
167 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
168#endif
169#ifdef VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME
170 VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME,
171#endif
172#ifdef VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME
173 VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
174#endif
175#ifdef VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME
176 VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
177#endif
178 };
179}
180
194inline SharedVulkanDevice createSharedVulkanDevice(
195 QVulkanInstance* inst, VkPhysicalDevice preferredPhysDev = VK_NULL_HANDLE)
196{
197 SharedVulkanDevice result;
198 if(!inst)
199 return result;
200
201 auto* funcs = inst->functions();
202 if(!funcs)
203 return result;
204
205 // --- Pick physical device (prefer discrete GPU) ---
206
207 uint32_t devCount = 0;
208 funcs->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr);
209 if(devCount == 0)
210 return result;
211
212 std::vector<VkPhysicalDevice> physDevs(devCount);
213 funcs->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, physDevs.data());
214
215 // Use the caller-specified physical device (matching QRhi's GPU),
216 // or fall back to the first one.
217 if(preferredPhysDev != VK_NULL_HANDLE)
218 result.physDev = preferredPhysDev;
219 else
220 result.physDev = physDevs[0];
221
222 uint32_t qfCount = 0;
223 funcs->vkGetPhysicalDeviceQueueFamilyProperties(
224 result.physDev, &qfCount, nullptr);
225 if(qfCount == 0)
226 return result;
227
228 std::vector<VkQueueFamilyProperties> qfProps(qfCount);
229 funcs->vkGetPhysicalDeviceQueueFamilyProperties(
230 result.physDev, &qfCount, qfProps.data());
231
232 bool foundGfx = false;
233 for(uint32_t i = 0; i < qfCount; i++)
234 {
235 if(!foundGfx && (qfProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
236 {
237 result.gfxQueueFamilyIdx = i;
238 foundGfx = true;
239 }
240 if(qfProps[i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR)
241 {
242 result.hasVideoDecodeQueue = true;
243 result.videoDecodeQueueFamilyIdx = i;
244 }
245 }
246
247 if(!foundGfx || !result.hasVideoDecodeQueue)
248 return {};
249
250 // --- Enumerate available device extensions ---
251
252 uint32_t extCount = 0;
253 funcs->vkEnumerateDeviceExtensionProperties(
254 result.physDev, nullptr, &extCount, nullptr);
255 std::vector<VkExtensionProperties> avail(extCount);
256 funcs->vkEnumerateDeviceExtensionProperties(
257 result.physDev, nullptr, &extCount, avail.data());
258
259 auto hasExt = [&](const char* name) {
260 for(auto& e : avail)
261 if(std::strcmp(e.extensionName, name) == 0)
262 return true;
263 return false;
264 };
265
266 // Only enable extensions that are actually available from the curated list.
267 // Use string literal pointers directly — DO NOT copy into std::string
268 // then take c_str(), as vector<string> reallocation invalidates all
269 // previous c_str() pointers (caused vkCreateDevice to get garbage names).
270 auto wantedExtensions = sharedVulkanDeviceExtensions();
271 std::vector<const char*> extPtrs;
272 for(auto* ext : wantedExtensions)
273 {
274 if(hasExt(ext))
275 {
276 result.enabledExtensions.push_back(ext);
277 extPtrs.push_back(ext); // ext is a string literal, always valid
278 }
279 }
280
281 // --- Query and enable ALL supported features ---
282
283 auto vkGetPhysicalDeviceFeatures2Fn
284 = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
285 inst->getInstanceProcAddr("vkGetPhysicalDeviceFeatures2"));
286 if(!vkGetPhysicalDeviceFeatures2Fn)
287 return {};
288
289 // Build feature chain: Vulkan 1.1 → 1.2 → 1.3
290 VkPhysicalDeviceVulkan13Features vk13{};
291 vk13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
292
293 VkPhysicalDeviceVulkan12Features vk12{};
294 vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
295 vk12.pNext = &vk13;
296
297 VkPhysicalDeviceVulkan11Features vk11{};
298 vk11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
299 vk11.pNext = &vk12;
300
301 VkPhysicalDeviceFeatures2 features2{};
302 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
303 features2.pNext = &vk11;
304
305 // Query fills all fields with what the device supports
306 vkGetPhysicalDeviceFeatures2Fn(result.physDev, &features2);
307
308 // --- Create queue infos (1 queue per family) ---
309
310 std::vector<VkDeviceQueueCreateInfo> queueInfos;
311 float priority = 1.0f;
312 for(uint32_t i = 0; i < qfCount; i++)
313 {
314 VkDeviceQueueCreateInfo qi{};
315 qi.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
316 qi.queueFamilyIndex = i;
317 qi.queueCount = 1;
318 qi.pQueuePriorities = &priority;
319 queueInfos.push_back(qi);
320
321 result.queueFamilies.push_back(
322 {i, 1, qfProps[i].queueFlags});
323 }
324
325 // --- Create VkDevice ---
326
327 VkDeviceCreateInfo devInfo{};
328 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
329 devInfo.pNext = &features2; // Features via pNext, not pEnabledFeatures
330 devInfo.queueCreateInfoCount = static_cast<uint32_t>(queueInfos.size());
331 devInfo.pQueueCreateInfos = queueInfos.data();
332 devInfo.enabledExtensionCount = static_cast<uint32_t>(extPtrs.size());
333 devInfo.ppEnabledExtensionNames = extPtrs.data();
334
335 auto vkCreateDeviceFn = reinterpret_cast<PFN_vkCreateDevice>(
336 inst->getInstanceProcAddr("vkCreateDevice"));
337 if(!vkCreateDeviceFn)
338 return {};
339
340 VkResult vkResult
341 = vkCreateDeviceFn(result.physDev, &devInfo, nullptr, &result.dev);
342 if(vkResult != VK_SUCCESS)
343 {
344 qDebug() << "createSharedVulkanDevice: vkCreateDevice failed:" << vkResult;
345 result.dev = VK_NULL_HANDLE;
346 return {};
347 }
348
349 // --- Get graphics queue ---
350
351 auto vkGetDeviceQueueFn = reinterpret_cast<PFN_vkGetDeviceQueue>(
352 inst->getInstanceProcAddr("vkGetDeviceQueue"));
353 if(vkGetDeviceQueueFn)
354 vkGetDeviceQueueFn(
355 result.dev, result.gfxQueueFamilyIdx, 0, &result.gfxQueue);
356
357 return result;
358}
359
360} // namespace score::gfx
361
362#endif // VK_KHR_video_decode_queue
363#endif // QT_HAS_VULKAN
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12
STL namespace.