Loading...
Searching...
No Matches
VkExternalMemoryHelpers.hpp
Go to the documentation of this file.
1#pragma once
2
24#include <score/gfx/Vulkan.hpp>
25
26#include <score_plugin_gfx_export.h>
27
28#if QT_HAS_VULKAN
29
30#include <vector>
31
32#include <vulkan/vulkan.h>
33
34#include <cstdint>
35#include <optional>
36
37class QVulkanInstance;
38
39namespace score::gfx::vkinterop
40{
41
49struct ExternalHandle
50{
51#if defined(_WIN32)
52 void* handle{nullptr}; // HANDLE
53#else
54 int fd{-1};
55#endif
56 VkExternalMemoryHandleTypeFlagBits type{};
57
58 bool isValid() const noexcept
59 {
60#if defined(_WIN32)
61 return handle != nullptr;
62#else
63 return fd >= 0;
64#endif
65 }
66
67 // OS handle encoded as a void* for the CUDA interop layer / DVP, which expect
68 // the Win32 HANDLE directly or the fd cast to pointer width (see
69 // CudaInterop.cpp's CUDA_EXTERNAL_MEMORY_HANDLE_DESC fill-in).
70 void* osHandle() const noexcept
71 {
72#if defined(_WIN32)
73 return handle;
74#else
75 return reinterpret_cast<void*>(static_cast<intptr_t>(fd));
76#endif
77 }
78};
79
80// Platform-preferred opaque external-memory handle type: OPAQUE_WIN32 on
81// Windows, OPAQUE_FD on Linux. Use this instead of hardcoding OPAQUE_FD so
82// the same export/import code compiles and runs on both platforms.
83inline constexpr VkExternalMemoryHandleTypeFlagBits kOpaqueHandleType =
84#if defined(_WIN32)
85 VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
86#else
87 VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
88#endif
89
91struct ExternalImageDesc
92{
93 VkFormat format{};
94 VkExtent3D extent{1, 1, 1};
95 VkImageUsageFlags usage{};
96 VkImageTiling tiling{VK_IMAGE_TILING_OPTIMAL};
97 VkExternalMemoryHandleTypeFlagBits handleType{};
98 // dedicated=true: required for D3D11_TEXTURE_KMT (Spout); harmless for OPAQUE_*.
99 bool dedicated{true};
100 bool preferDeviceLocal{true};
101
116 const uint64_t* drmModifiers{};
117 uint32_t drmModifierCount{};
118};
119
121struct ExternalBufferDesc
122{
123 VkDeviceSize size{};
124 VkBufferUsageFlags usage{};
125 VkExternalMemoryHandleTypeFlagBits handleType{};
126 bool dedicated{true};
127};
128
130struct VulkanCtx
131{
132 VkInstance instance{VK_NULL_HANDLE};
133 VkPhysicalDevice physDev{VK_NULL_HANDLE};
134 VkDevice dev{VK_NULL_HANDLE};
135 QVulkanInstance* qInst{}; // for getInstanceProcAddr (PFN resolution)
136};
137
143SCORE_PLUGIN_GFX_EXPORT void setDeviceTimelineSemaphoresEnabled(bool enabled);
144SCORE_PLUGIN_GFX_EXPORT bool deviceTimelineSemaphoresEnabled();
145
146struct ExternalImage
147{
148 VkImage image{VK_NULL_HANDLE};
149 VkDeviceMemory memory{VK_NULL_HANDLE};
150 VkDeviceSize size{0};
153 uint64_t drmModifier{(1ull << 56) - 1};
154};
155
156struct ExternalBuffer
157{
158 VkBuffer buffer{VK_NULL_HANDLE};
159 VkDeviceMemory memory{VK_NULL_HANDLE};
160 VkDeviceSize size{0};
161};
162
163// =============================================================================
164// RDMA (VK_NV_external_memory_rdma) — GPU VRAM a third-party DMA engine
165// (capture card with GPUDirect RDMA) can target via a remote address. Used by
166// the RdmaGpuBuffer allocator. All functions return empty/nullopt when
167// the extension is unavailable in the headers or at runtime (safe fallback).
168// =============================================================================
169
170struct RdmaBuffer
171{
172 VkBuffer buffer{VK_NULL_HANDLE};
173 VkDeviceMemory memory{VK_NULL_HANDLE};
174 VkDeviceSize size{0};
175 std::uint64_t remoteAddress{0};
176};
177
181SCORE_PLUGIN_GFX_EXPORT std::optional<RdmaBuffer>
182createRdmaBuffer(const VulkanCtx&, VkDeviceSize size);
183
186SCORE_PLUGIN_GFX_EXPORT std::optional<std::uint64_t>
187getMemoryRemoteAddress(const VulkanCtx&, VkDeviceMemory);
188
189SCORE_PLUGIN_GFX_EXPORT void destroyRdma(const VulkanCtx&, RdmaBuffer&);
190
191// =============================================================================
192// EXPORT — score allocates the resource, hands the handle to a peer.
193// =============================================================================
194
202SCORE_PLUGIN_GFX_EXPORT std::optional<ExternalImage>
203createExportableImage(const VulkanCtx&, const ExternalImageDesc&);
204
210SCORE_PLUGIN_GFX_EXPORT std::optional<ExternalBuffer>
211createExportableBuffer(const VulkanCtx&, const ExternalBufferDesc&);
212
222SCORE_PLUGIN_GFX_EXPORT std::optional<ExternalHandle> exportMemoryHandle(
223 const VulkanCtx&, VkDeviceMemory, VkExternalMemoryHandleTypeFlagBits);
224
225// =============================================================================
226// IMPORT — score consumes a handle produced by a peer (Spout, CUDA, etc).
227// =============================================================================
228
241SCORE_PLUGIN_GFX_EXPORT std::optional<ExternalImage> importExternalImage(
242 const VulkanCtx&, const ExternalImageDesc&, const ExternalHandle&);
243
247SCORE_PLUGIN_GFX_EXPORT std::optional<ExternalBuffer> importExternalBuffer(
248 const VulkanCtx&, const ExternalBufferDesc&, const ExternalHandle&);
249
250// =============================================================================
251// Cleanup — symmetric helpers. Out-of-line because they dispatch through
252// QVulkanInstance::deviceFunctions(), which would otherwise drag
253// <QVulkanDeviceFunctions> into every consumer of this header.
254// =============================================================================
255
256SCORE_PLUGIN_GFX_EXPORT void destroyExternal(const VulkanCtx& v, ExternalImage& img);
257SCORE_PLUGIN_GFX_EXPORT void destroyExternal(const VulkanCtx& v, ExternalBuffer& buf);
258
259// =============================================================================
260// Layout transition — one-time image layout change via a transient command
261// buffer (create pool + buffer, record a VkImageMemoryBarrier, submit, wait
262// idle). Used by the RDMA capture/output paths to move a freshly-created
263// exportable VkImage UNDEFINED -> GENERAL once, so the QRhi-adopted texture
264// (told GENERAL via createFrom) and CUDA (which reads/writes the memory) agree.
265//
266// Broad, conservative access/stage masks cover both the capture (shader-read)
267// and output (transfer-write) uses; the following vkQueueWaitIdle makes the
268// choice of masks immaterial to correctness for a one-time init transition.
269// @p queue / @p queueFamily are the graphics queue + family the barrier submits
270// on (from QRhiVulkanNativeHandles). Returns false on any Vulkan failure.
271// =============================================================================
272SCORE_PLUGIN_GFX_EXPORT bool transitionImageLayout(
273 const VulkanCtx& v, VkQueue queue, std::uint32_t queueFamily, VkImage image,
274 VkImageLayout oldLayout, VkImageLayout newLayout);
275
276// =============================================================================
277// DMA-BUF modifier capability probes
278// =============================================================================
279
291bool can_import_dmabuf_modifier(
292 const VulkanCtx& v, VkFormat format, std::uint64_t drm_modifier,
293 std::uint32_t width, std::uint32_t height) noexcept;
294
302std::vector<std::uint64_t> supported_dmabuf_modifiers(
303 const VulkanCtx& v, VkFormat format) noexcept;
304
305} // namespace score::gfx::vkinterop
306
307#endif // QT_HAS_VULKAN