3#include <score/gfx/Vulkan.hpp>
11#if __has_include(<libavutil/hwcontext_cuda.h>)
15#include <libavutil/hwcontext_cuda.h>
16#define SCORE_HAS_CUDA_HWCONTEXT 1
20#if defined(SCORE_HAS_CUDA_HWCONTEXT) && QT_HAS_VULKAN && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
22#include <Gfx/Graph/decoders/ColorSpace.hpp>
23#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
24#include <Gfx/Graph/decoders/NV12.hpp>
25#include <Gfx/Graph/decoders/P010.hpp>
26#include <Video/GpuFormats.hpp>
28#include <QtGui/private/qrhivulkan_p.h>
29#include <qvulkanfunctions.h>
30#include <vulkan/vulkan.h>
34#ifndef VK_USE_PLATFORM_WIN32_KHR
35#define VK_USE_PLATFORM_WIN32_KHR
37#include <vulkan/vulkan_win32.h>
44#include <libavformat/avformat.h>
45#include <libavutil/hwcontext.h>
71struct HWCudaVulkanDecoder : GPUVideoDecoder
74 PixelFormatInfo m_fmt;
78 VkDevice m_dev{VK_NULL_HANDLE};
79 VkPhysicalDevice m_physDev{VK_NULL_HANDLE};
80 QVulkanInstance* m_qInst{};
84 CUstream m_cuStream{};
92 VkImage image{VK_NULL_HANDLE};
93 VkDeviceMemory memory{VK_NULL_HANDLE};
94 CUexternalMemory cuExtMem{};
95 CUmipmappedArray cuMipArray{};
97 VkDeviceSize memSize{};
99 PlaneResources m_planes[2]{};
101 bool m_interopReady{
false};
105 static bool isAvailable(QRhi& rhi, AVBufferRef* hwDeviceCtx)
107 if(rhi.backend() != QRhi::Vulkan)
109 auto* nh =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
110 if(!nh || !nh->dev || !nh->physDev || !nh->inst)
113 if(!nh->inst->getInstanceProcAddr(
"vkGetMemoryWin32HandleKHR"))
116 if(!nh->inst->getInstanceProcAddr(
"vkGetMemoryFdKHR"))
123 auto* devCtx =
reinterpret_cast<AVHWDeviceContext*
>(hwDeviceCtx->data);
124 if(devCtx->type != AV_HWDEVICE_TYPE_CUDA)
132 explicit HWCudaVulkanDecoder(
138 auto* nh =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
140 m_physDev = nh->physDev;
144 auto* devCtx =
reinterpret_cast<AVHWDeviceContext*
>(hwDeviceCtx->data);
145 auto* cudaDevCtx =
static_cast<AVCUDADeviceContext*
>(devCtx->hwctx);
146 m_cuCtx = cudaDevCtx->cuda_ctx;
147 m_cuStream = cudaDevCtx->stream;
152 ~HWCudaVulkanDecoder()
override { cleanup(); }
156 if(m_cuCtx && m_cu.ctxPush)
158 m_cu.ctxPush(m_cuCtx);
159 for(
auto& p : m_planes)
162 m_cu.destroyMipArray(p.cuMipArray);
164 m_cu.destroyExtMem(p.cuExtMem);
173 vkinterop::VulkanCtx vctx{
174 m_qInst ? m_qInst->vkInstance() : VK_NULL_HANDLE, m_physDev, m_dev,
176 for(
auto& p : m_planes)
178 vkinterop::ExternalImage img{p.image, p.memory, p.memSize};
179 vkinterop::destroyExternal(vctx, img);
180 p.image = VK_NULL_HANDLE;
181 p.memory = VK_NULL_HANDLE;
184 m_interopReady =
false;
191 std::pair<QShader, QShader> init(RenderList& r)
override
193 auto& rhi = *r.state.rhi;
194 const auto w = decoder.width, h = decoder.height;
200 auto tex = rhi.newTexture(QRhiTexture::R16, {w, h}, 1, QRhiTexture::Flag{});
202 auto sampler = rhi.newSampler(
203 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
204 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
206 samplers.push_back({sampler, tex});
210 = rhi.newTexture(QRhiTexture::RG16, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
212 auto sampler = rhi.newSampler(
213 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
214 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
216 samplers.push_back({sampler, tex});
220 if(!setupPlane(0, VK_FORMAT_R16_UNORM, w, h, 1, 2)
221 || !setupPlane(1, VK_FORMAT_R16G16_UNORM, w / 2, h / 2, 2, 2))
223 qDebug() <<
"HWCudaVulkanDecoder: interop setup failed";
230 for(
auto& s : samplers)
236 r.state, vertexShader(),
237 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
243 auto tex = rhi.newTexture(QRhiTexture::R8, {w, h}, 1, QRhiTexture::Flag{});
245 auto sampler = rhi.newSampler(
246 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
247 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
249 samplers.push_back({sampler, tex});
253 = rhi.newTexture(QRhiTexture::RG8, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
255 auto sampler = rhi.newSampler(
256 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
257 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
259 samplers.push_back({sampler, tex});
263 if(!setupPlane(0, VK_FORMAT_R8_UNORM, w, h, 1, 1)
264 || !setupPlane(1, VK_FORMAT_R8G8_UNORM, w / 2, h / 2, 2, 1))
266 qDebug() <<
"HWCudaVulkanDecoder: interop setup failed";
273 for(
auto& s : samplers)
278 QString frag = NV12Decoder::nv12_filter_prologue;
279 frag +=
" vec3 yuv = vec3(y, u, v);\n";
280 frag += NV12Decoder::nv12_filter_epilogue;
282 r.state, vertexShader(), frag.arg(
"").arg(colorMatrix(decoder)));
290 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
292#if LIBAVUTIL_VERSION_MAJOR >= 57
296 if(!Video::formatIsHardwareDecoded(
static_cast<AVPixelFormat
>(frame.format)))
299 const int w = decoder.width;
300 const int h = decoder.height;
301 const int bpc = m_fmt.is10bit() ? 2 : 1;
303 m_cu.ctxPush(m_cuCtx);
308 cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
309 cpy.srcDevice =
reinterpret_cast<CUdeviceptr
>(frame.data[0]);
310 cpy.srcPitch =
static_cast<size_t>(frame.linesize[0]);
311 cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
312 cpy.dstArray = m_planes[0].cuArray;
313 cpy.WidthInBytes =
static_cast<size_t>(w * 1 * bpc);
314 cpy.Height =
static_cast<size_t>(h);
315 m_cu.memcpy2DAsync(&cpy, m_cuStream);
321 cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
322 cpy.srcDevice =
reinterpret_cast<CUdeviceptr
>(frame.data[1]);
323 cpy.srcPitch =
static_cast<size_t>(frame.linesize[1]);
324 cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
325 cpy.dstArray = m_planes[1].cuArray;
326 cpy.WidthInBytes =
static_cast<size_t>((w / 2) * 2 * bpc);
327 cpy.Height =
static_cast<size_t>(h / 2);
328 m_cu.memcpy2DAsync(&cpy, m_cuStream);
332 m_cu.streamSync(m_cuStream);
340 samplers[0].texture->setNativeLayout(VK_IMAGE_LAYOUT_GENERAL);
341 samplers[1].texture->setNativeLayout(VK_IMAGE_LAYOUT_GENERAL);
350 int idx, VkFormat vkFmt,
int w,
int h,
351 int numChannels,
int bytesPerChannel)
353 auto& plane = m_planes[idx];
356 constexpr auto kHandleType
357 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
358 constexpr auto kCudaHandleType
359 = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
361 constexpr auto kHandleType
362 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
363 constexpr auto kCudaHandleType
364 = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;
367 vkinterop::VulkanCtx vctx{m_qInst->vkInstance(), m_physDev, m_dev, m_qInst};
370 vkinterop::ExternalImageDesc imgDesc{};
371 imgDesc.format = vkFmt;
373 = {
static_cast<uint32_t
>(w),
static_cast<uint32_t
>(h), 1u};
375 = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
376 imgDesc.tiling = VK_IMAGE_TILING_OPTIMAL;
377 imgDesc.handleType = kHandleType;
378 imgDesc.dedicated =
true;
379 imgDesc.preferDeviceLocal =
true;
381 auto extImg = vkinterop::createExportableImage(vctx, imgDesc);
385 plane.image = extImg->image;
386 plane.memory = extImg->memory;
387 plane.memSize = extImg->size;
390 auto exported = vkinterop::exportMemoryHandle(vctx, plane.memory, kHandleType);
393 vkinterop::ExternalImage tmp{plane.image, plane.memory, plane.memSize};
394 vkinterop::destroyExternal(vctx, tmp);
395 plane.image = VK_NULL_HANDLE;
396 plane.memory = VK_NULL_HANDLE;
404 m_cu.ctxPush(m_cuCtx);
407 memDesc.type = kCudaHandleType;
409 memDesc.handle.win32.handle = exported->handle;
411 memDesc.handle.fd = exported->fd;
413 memDesc.size = plane.memSize;
415 if(m_cu.importExtMem(&plane.cuExtMem, &memDesc) != CUDA_SUCCESS)
418 CloseHandle(exported->handle);
420 ::close(exported->fd);
428 CloseHandle(exported->handle);
437 mipDesc.arrayDesc.Width =
static_cast<size_t>(w);
438 mipDesc.arrayDesc.Height =
static_cast<size_t>(h);
439 mipDesc.arrayDesc.Depth = 0;
440 mipDesc.arrayDesc.Format = (bytesPerChannel == 2)
441 ? CU_AD_FORMAT_UNSIGNED_INT16
442 : CU_AD_FORMAT_UNSIGNED_INT8;
443 mipDesc.arrayDesc.NumChannels =
static_cast<unsigned int>(numChannels);
444 mipDesc.arrayDesc.Flags = 0;
445 mipDesc.numLevels = 1;
447 if(m_cu.getMapArray(&plane.cuMipArray, plane.cuExtMem, &mipDesc)
454 if(m_cu.getLevel(&plane.cuArray, plane.cuMipArray, 0) != CUDA_SUCCESS)
466 samplers[idx].texture->createFrom(QRhiTexture::NativeTexture{
467 quint64(plane.image), VK_IMAGE_LAYOUT_GENERAL});
469 m_interopReady = (idx == 1);
Shared dlopen'd CUDA driver-API table for score-plugin-gfx.
Vulkan external-memory image/buffer create + export + import helpers.
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag, int multiViewCount)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1238
Definition CudaFunctions.hpp:235
Definition CudaFunctions.hpp:271
Definition CudaFunctions.hpp:279