3#include <score/gfx/Vulkan.hpp>
9#include <Gfx/Graph/VulkanVideoDevice.hpp>
12#if __has_include(<libavutil/hwcontext_cuda.h>)
16#include <libavutil/hwcontext_cuda.h>
17#define SCORE_HAS_CUDA_HWCONTEXT 1
21#if defined(SCORE_HAS_CUDA_HWCONTEXT) && QT_HAS_VULKAN && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
23#include <Gfx/Graph/decoders/ColorSpace.hpp>
24#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
25#include <Gfx/Graph/decoders/NV12.hpp>
26#include <Gfx/Graph/decoders/P010.hpp>
27#include <Video/GpuFormats.hpp>
29#include <QtGui/private/qrhivulkan_p.h>
30#include <qvulkanfunctions.h>
31#include <vulkan/vulkan.h>
35#ifndef VK_USE_PLATFORM_WIN32_KHR
36#define VK_USE_PLATFORM_WIN32_KHR
38#include <vulkan/vulkan_win32.h>
45#include <libavformat/avformat.h>
46#include <libavutil/hwcontext.h>
72struct HWCudaVulkanDecoder : GPUVideoDecoder
75 PixelFormatInfo m_fmt;
79 VkDevice m_dev{VK_NULL_HANDLE};
80 VkPhysicalDevice m_physDev{VK_NULL_HANDLE};
81 QVulkanInstance* m_qInst{};
85 CUstream m_cuStream{};
93 VkImage image{VK_NULL_HANDLE};
94 VkDeviceMemory memory{VK_NULL_HANDLE};
95 CUexternalMemory cuExtMem{};
96 CUmipmappedArray cuMipArray{};
98 VkDeviceSize memSize{};
100 PlaneResources m_planes[2]{};
102 bool m_interopReady{
false};
106 static bool isAvailable(QRhi& rhi, AVBufferRef* hwDeviceCtx)
108 if(rhi.backend() != QRhi::Vulkan)
110 auto* nh =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
111 if(!nh || !nh->dev || !nh->physDev || !nh->inst)
115 if(!vulkanDeviceCommand(rhi,
"vkGetMemoryWin32HandleKHR"))
118 if(!vulkanDeviceCommand(rhi,
"vkGetMemoryFdKHR"))
125 auto* devCtx =
reinterpret_cast<AVHWDeviceContext*
>(hwDeviceCtx->data);
126 if(devCtx->type != AV_HWDEVICE_TYPE_CUDA)
134 explicit HWCudaVulkanDecoder(
140 auto* nh =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
142 m_physDev = nh->physDev;
146 auto* devCtx =
reinterpret_cast<AVHWDeviceContext*
>(hwDeviceCtx->data);
147 auto* cudaDevCtx =
static_cast<AVCUDADeviceContext*
>(devCtx->hwctx);
148 m_cuCtx = cudaDevCtx->cuda_ctx;
149 m_cuStream = cudaDevCtx->stream;
154 ~HWCudaVulkanDecoder()
override { cleanup(); }
158 if(m_cuCtx && m_cu.ctxPush)
160 m_cu.ctxPush(m_cuCtx);
161 for(
auto& p : m_planes)
164 m_cu.destroyMipArray(p.cuMipArray);
166 m_cu.destroyExtMem(p.cuExtMem);
175 vkinterop::VulkanCtx vctx{
176 m_qInst ? m_qInst->vkInstance() : VK_NULL_HANDLE, m_physDev, m_dev,
178 for(
auto& p : m_planes)
180 vkinterop::ExternalImage img{p.image, p.memory, p.memSize};
181 vkinterop::destroyExternal(vctx, img);
182 p.image = VK_NULL_HANDLE;
183 p.memory = VK_NULL_HANDLE;
186 m_interopReady =
false;
193 std::pair<QShader, QShader> init(RenderList& r)
override
195 auto& rhi = *r.state.rhi;
196 const auto w = decoder.width, h = decoder.height;
202 auto tex = rhi.newTexture(QRhiTexture::R16, {w, h}, 1, QRhiTexture::Flag{});
204 auto sampler = rhi.newSampler(
205 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
206 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
208 samplers.push_back({sampler, tex});
212 = rhi.newTexture(QRhiTexture::RG16, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
214 auto sampler = rhi.newSampler(
215 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
216 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
218 samplers.push_back({sampler, tex});
222 if(!setupPlane(0, VK_FORMAT_R16_UNORM, w, h, 1, 2)
223 || !setupPlane(1, VK_FORMAT_R16G16_UNORM, w / 2, h / 2, 2, 2))
225 qDebug() <<
"HWCudaVulkanDecoder: interop setup failed";
232 for(
auto& s : samplers)
238 r.state, vertexShader(),
239 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
245 auto tex = rhi.newTexture(QRhiTexture::R8, {w, h}, 1, QRhiTexture::Flag{});
247 auto sampler = rhi.newSampler(
248 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
249 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
251 samplers.push_back({sampler, tex});
255 = rhi.newTexture(QRhiTexture::RG8, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
257 auto sampler = rhi.newSampler(
258 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
259 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
261 samplers.push_back({sampler, tex});
265 if(!setupPlane(0, VK_FORMAT_R8_UNORM, w, h, 1, 1)
266 || !setupPlane(1, VK_FORMAT_R8G8_UNORM, w / 2, h / 2, 2, 1))
268 qDebug() <<
"HWCudaVulkanDecoder: interop setup failed";
275 for(
auto& s : samplers)
280 QString frag = NV12Decoder::nv12_filter_prologue;
281 frag +=
" vec3 yuv = vec3(y, u, v);\n";
282 frag += NV12Decoder::nv12_filter_epilogue;
284 r.state, vertexShader(), frag.arg(
"").arg(colorMatrix(decoder)));
292 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
294#if LIBAVUTIL_VERSION_MAJOR >= 57
298 if(!Video::formatIsHardwareDecoded(
static_cast<AVPixelFormat
>(frame.format)))
301 const int w = decoder.width;
302 const int h = decoder.height;
303 const int bpc = m_fmt.is10bit() ? 2 : 1;
305 m_cu.ctxPush(m_cuCtx);
310 cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
311 cpy.srcDevice =
reinterpret_cast<CUdeviceptr
>(frame.data[0]);
312 cpy.srcPitch =
static_cast<size_t>(frame.linesize[0]);
313 cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
314 cpy.dstArray = m_planes[0].cuArray;
315 cpy.WidthInBytes =
static_cast<size_t>(w * 1 * bpc);
316 cpy.Height =
static_cast<size_t>(h);
317 m_cu.memcpy2DAsync(&cpy, m_cuStream);
323 cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
324 cpy.srcDevice =
reinterpret_cast<CUdeviceptr
>(frame.data[1]);
325 cpy.srcPitch =
static_cast<size_t>(frame.linesize[1]);
326 cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
327 cpy.dstArray = m_planes[1].cuArray;
328 cpy.WidthInBytes =
static_cast<size_t>((w / 2) * 2 * bpc);
329 cpy.Height =
static_cast<size_t>(h / 2);
330 m_cu.memcpy2DAsync(&cpy, m_cuStream);
334 m_cu.streamSync(m_cuStream);
342 samplers[0].texture->setNativeLayout(VK_IMAGE_LAYOUT_GENERAL);
343 samplers[1].texture->setNativeLayout(VK_IMAGE_LAYOUT_GENERAL);
352 int idx, VkFormat vkFmt,
int w,
int h,
353 int numChannels,
int bytesPerChannel)
355 auto& plane = m_planes[idx];
358 constexpr auto kHandleType
359 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
360 constexpr auto kCudaHandleType
361 = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
363 constexpr auto kHandleType
364 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
365 constexpr auto kCudaHandleType
366 = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;
369 vkinterop::VulkanCtx vctx{m_qInst->vkInstance(), m_physDev, m_dev, m_qInst};
372 vkinterop::ExternalImageDesc imgDesc{};
373 imgDesc.format = vkFmt;
375 = {
static_cast<uint32_t
>(w),
static_cast<uint32_t
>(h), 1u};
377 = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
378 imgDesc.tiling = VK_IMAGE_TILING_OPTIMAL;
379 imgDesc.handleType = kHandleType;
380 imgDesc.dedicated =
true;
381 imgDesc.preferDeviceLocal =
true;
383 auto extImg = vkinterop::createExportableImage(vctx, imgDesc);
387 plane.image = extImg->image;
388 plane.memory = extImg->memory;
389 plane.memSize = extImg->size;
392 auto exported = vkinterop::exportMemoryHandle(vctx, plane.memory, kHandleType);
395 vkinterop::ExternalImage tmp{plane.image, plane.memory, plane.memSize};
396 vkinterop::destroyExternal(vctx, tmp);
397 plane.image = VK_NULL_HANDLE;
398 plane.memory = VK_NULL_HANDLE;
406 m_cu.ctxPush(m_cuCtx);
409 memDesc.type = kCudaHandleType;
411 memDesc.handle.win32.handle = exported->handle;
413 memDesc.handle.fd = exported->fd;
415 memDesc.size = plane.memSize;
417 if(m_cu.importExtMem(&plane.cuExtMem, &memDesc) != CUDA_SUCCESS)
420 CloseHandle(exported->handle);
422 ::close(exported->fd);
430 CloseHandle(exported->handle);
439 mipDesc.arrayDesc.Width =
static_cast<size_t>(w);
440 mipDesc.arrayDesc.Height =
static_cast<size_t>(h);
441 mipDesc.arrayDesc.Depth = 0;
442 mipDesc.arrayDesc.Format = (bytesPerChannel == 2)
443 ? CU_AD_FORMAT_UNSIGNED_INT16
444 : CU_AD_FORMAT_UNSIGNED_INT8;
445 mipDesc.arrayDesc.NumChannels =
static_cast<unsigned int>(numChannels);
446 mipDesc.arrayDesc.Flags = 0;
447 mipDesc.numLevels = 1;
449 if(m_cu.getMapArray(&plane.cuMipArray, plane.cuExtMem, &mipDesc)
456 if(m_cu.getLevel(&plane.cuArray, plane.cuMipArray, 0) != CUDA_SUCCESS)
468 samplers[idx].texture->createFrom(QRhiTexture::NativeTexture{
469 quint64(plane.image), VK_IMAGE_LAYOUT_GENERAL});
471 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:1707
Definition CudaFunctions.hpp:235
Definition CudaFunctions.hpp:271
Definition CudaFunctions.hpp:279