3#include <score/gfx/Vulkan.hpp>
8#if __has_include(<libavutil/hwcontext_vulkan.h>)
9#include <libavutil/hwcontext_vulkan.h>
10#define SCORE_HAS_VULKAN_HWCONTEXT_SHARED 1
14#if defined(SCORE_HAS_VULKAN_HWCONTEXT_SHARED) && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
16#include <Gfx/Graph/decoders/ColorSpace.hpp>
17#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
18#include <Gfx/Graph/decoders/NV12.hpp>
19#include <Gfx/Graph/decoders/P010.hpp>
20#include <Gfx/Graph/decoders/YUV420.hpp>
21#include <Gfx/Graph/decoders/YUV420P10.hpp>
22#include <Gfx/Graph/decoders/YUV422.hpp>
23#include <Gfx/Graph/decoders/YUV422P10.hpp>
24#include <Gfx/Graph/decoders/YUV444.hpp>
25#include <Gfx/Graph/decoders/YUV444P10.hpp>
26#include <Gfx/Graph/decoders/YUVA444.hpp>
27#include <Video/GpuFormats.hpp>
30#include <QtGui/private/qrhivulkan_p.h>
31#include <qvulkanfunctions.h>
32#include <vulkan/vulkan.h>
35#include <libavformat/avformat.h>
36#include <libavutil/hwcontext.h>
37#include <libavutil/pixdesc.h>
53struct HWVulkanSharedDecoder : GPUVideoDecoder
56 PixelFormatInfo m_fmt;
65 VkDevice m_dev{VK_NULL_HANDLE};
66 VkPhysicalDevice m_physDev{VK_NULL_HANDLE};
67 QVulkanFunctions* m_funcs{};
68 QVulkanDeviceFunctions* m_dfuncs{};
69 PFN_vkWaitSemaphores m_vkWaitSemaphores{};
70 uint32_t m_gfxQueueFamilyIdx{0};
71 VkQueue m_gfxQueue{VK_NULL_HANDLE};
74 VkCommandPool m_cmdPool{VK_NULL_HANDLE};
75 VkCommandBuffer m_cmdBuf{VK_NULL_HANDLE};
76 VkFence m_fence{VK_NULL_HANDLE};
77 bool m_cmdReady{
false};
80 static constexpr int NumSlots = 3;
84 VkImageView planeViews[4]{};
87 FrameSlot m_slots[NumSlots]{};
92 static bool isAvailable(QRhi& rhi)
94 if(rhi.backend() != QRhi::Vulkan)
97 =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
98 if(!nh || !nh->dev || !nh->physDev || !nh->inst)
100 return nh->inst->getInstanceProcAddr(
"vkWaitSemaphores") !=
nullptr;
103 explicit HWVulkanSharedDecoder(
112 =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
114 m_physDev = nh->physDev;
115 m_funcs = nh->inst->functions();
116 m_dfuncs = nh->inst->deviceFunctions(m_dev);
117 m_gfxQueueFamilyIdx = nh->gfxQueueFamilyIdx;
121 if(
auto getDevProc =
reinterpret_cast<PFN_vkGetDeviceProcAddr
>(
122 nh->inst->getInstanceProcAddr(
"vkGetDeviceProcAddr")))
123 m_vkWaitSemaphores =
reinterpret_cast<PFN_vkWaitSemaphores
>(
124 getDevProc(m_dev,
"vkWaitSemaphores"));
125 m_dfuncs->vkGetDeviceQueue(
126 m_dev, m_gfxQueueFamilyIdx, 0, &m_gfxQueue);
129 void release(RenderList& r)
override
134 for(
auto& s : samplers)
135 if(auto* vkTex = static_cast<QVkTexture*>(s.texture); vkTex && !vkTex->owns)
137 vkTex->image = VK_NULL_HANDLE;
138 vkTex->imageView = VK_NULL_HANDLE;
140 GPUVideoDecoder::release(r);
143 ~HWVulkanSharedDecoder()
override
150 if(m_gfxQueue != VK_NULL_HANDLE)
151 m_dfuncs->vkQueueWaitIdle(m_gfxQueue);
153 for(
auto& slot : m_slots)
155 if(m_fence != VK_NULL_HANDLE)
156 m_dfuncs->vkDestroyFence(m_dev, m_fence,
nullptr);
157 if(m_cmdPool != VK_NULL_HANDLE)
158 m_dfuncs->vkDestroyCommandPool(m_dev, m_cmdPool,
nullptr);
161 void cleanupSlot(FrameSlot& slot)
163 for(
int i = 0; i < slot.numViews; i++)
165 if(slot.planeViews[i] != VK_NULL_HANDLE)
167 m_dfuncs->vkDestroyImageView(m_dev, slot.planeViews[i],
nullptr);
168 slot.planeViews[i] = VK_NULL_HANDLE;
174 av_frame_free(&slot.frameRef);
175 slot.frameRef =
nullptr;
179 bool setupCommandInfra()
181 VkCommandPoolCreateInfo poolInfo{};
182 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
183 poolInfo.queueFamilyIndex = m_gfxQueueFamilyIdx;
184 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
185 if(m_dfuncs->vkCreateCommandPool(m_dev, &poolInfo,
nullptr, &m_cmdPool)
189 VkCommandBufferAllocateInfo allocInfo{};
190 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
191 allocInfo.commandPool = m_cmdPool;
192 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
193 allocInfo.commandBufferCount = 1;
194 if(m_dfuncs->vkAllocateCommandBuffers(m_dev, &allocInfo, &m_cmdBuf)
198 VkFenceCreateInfo fenceInfo{};
199 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
200 if(m_dfuncs->vkCreateFence(m_dev, &fenceInfo,
nullptr, &m_fence)
214 QString cropVertexShader()
const
216 QString vtx = vertexShader();
217 const int w = decoder.width, h = decoder.height;
218 if(m_codedW > w || m_codedH > h)
220 const double sx = m_codedW > w ? double(w) / m_codedW : 1.;
221 const double sy = m_codedH > h ? double(h) / m_codedH : 1.;
223 "v_texcoord = texcoord;",
224 QString(
"v_texcoord = texcoord * vec2(%1, %2);")
226 .arg(sy, 0,
'f', 9));
231 std::pair<QShader, QShader> init(RenderList& r)
override
233 auto& rhi = *r.state.rhi;
234 const auto w = decoder.width, h = decoder.height;
235 const bool is10 = m_fmt.is10bit();
236 auto texFmt = is10 ? QRhiTexture::R16 : QRhiTexture::R8;
237 int chromaW = AV_CEIL_RSHIFT(w, m_fmt.log2ChromaW);
238 int chromaH = AV_CEIL_RSHIFT(h, m_fmt.log2ChromaH);
245 m_numPlanes = m_fmt.numPlanes;
249 auto uvFmt = is10 ? QRhiTexture::RG16 : QRhiTexture::RG8;
250 createTex(rhi, texFmt, w, h);
251 createTex(rhi, uvFmt, chromaW, chromaH);
255 r.state, cropVertexShader(),
256 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
259 QString frag = NV12Decoder::nv12_filter_prologue;
260 frag +=
" vec3 yuv = vec3(y, u, v);\n";
261 frag += NV12Decoder::nv12_filter_epilogue;
263 r.state, cropVertexShader(),
264 frag.arg(
"").arg(colorMatrix(decoder)));
267 else if(m_fmt.hasAlpha)
270 createTex(rhi, texFmt, w, h);
271 createTex(rhi, texFmt, chromaW, chromaH);
272 createTex(rhi, texFmt, chromaW, chromaH);
273 createTex(rhi, texFmt, w, h);
278 r.state, cropVertexShader(),
279 QString(YUVA444Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
285 double scale = 65535.0 / (255.0 * (1 << (m_fmt.bitDepth - 8)));
286 QString frag = QString(R
"_(#version 450
288)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
290layout(binding=3) uniform sampler2D y_tex;
291layout(binding=4) uniform sampler2D u_tex;
292layout(binding=5) uniform sampler2D v_tex;
293layout(binding=6) uniform sampler2D a_tex;
295layout(location = 0) in vec2 v_texcoord;
296layout(location = 0) out vec4 fragColor;
300vec4 processTexture(vec4 tex) {
301 vec4 processed = convert_to_rgb(tex);
308 float sc = float(%3);
309 float y = sc * texture(y_tex, v_texcoord).r;
310 float u = sc * texture(u_tex, v_texcoord).r;
311 float v = sc * texture(v_tex, v_texcoord).r;
312 float a = sc * texture(a_tex, v_texcoord).r;
314 vec4 rgb = processTexture(vec4(y,u,v, 1.));
315 fragColor = vec4(rgb.rgb, a);
317)_").arg("").arg(colorMatrix(decoder)).arg(scale, 0,
'f', 6);
325 createTex(rhi, texFmt, w, h);
326 createTex(rhi, texFmt, chromaW, chromaH);
327 createTex(rhi, texFmt, chromaW, chromaH);
334 if(m_fmt.log2ChromaW == 1 && m_fmt.log2ChromaH == 0)
336 r.state, cropVertexShader(),
337 QString(YUV422Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
338 else if(m_fmt.log2ChromaW == 0 && m_fmt.log2ChromaH == 0)
340 r.state, cropVertexShader(),
341 QString(YUV444Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
343 r.state, cropVertexShader(),
344 QString(YUV420Decoder::frag)
346 .arg(colorMatrix(decoder))
353 double scale = 65535.0 / ((1 << m_fmt.bitDepth) - 1);
354 QString frag = QString(R
"_(#version 450
356)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
358layout(binding=3) uniform sampler2D y_tex;
359layout(binding=4) uniform sampler2D u_tex;
360layout(binding=5) uniform sampler2D v_tex;
362layout(location = 0) in vec2 v_texcoord;
363layout(location = 0) out vec4 fragColor;
367vec4 processTexture(vec4 tex) {
368 vec4 processed = convert_to_rgb(tex);
375 float sc = float(%3);
376 float y = sc * texture(y_tex, v_texcoord).r;
377 float u = sc * texture(u_tex, v_texcoord).r;
378 float v = sc * texture(v_tex, v_texcoord).r;
380 fragColor = processTexture(vec4(y,u,v, 1.));
382)_").arg("").arg(colorMatrix(decoder)).arg(scale, 0,
'f', 6);
393 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
395#if LIBAVUTIL_VERSION_MAJOR >= 57
396 if(!Video::formatIsHardwareDecoded(
397 static_cast<AVPixelFormat
>(frame.format)))
400 auto* vkf =
reinterpret_cast<AVVkFrame*
>(frame.data[0]);
401 if(!vkf || vkf->img[0] == VK_NULL_HANDLE)
404 if(!m_cmdReady && !setupCommandInfra())
408 if(m_vkWaitSemaphores && vkf->sem[0] != VK_NULL_HANDLE)
411 for(
int i = 0; i < 4; i++)
412 if(vkf->sem[i] != VK_NULL_HANDLE)
416 VkSemaphoreWaitInfo waitInfo{};
417 waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
418 waitInfo.semaphoreCount =
static_cast<uint32_t
>(numSems);
419 waitInfo.pSemaphores = vkf->sem;
420 waitInfo.pValues = vkf->sem_value;
421 m_vkWaitSemaphores(m_dev, &waitInfo, UINT64_MAX);
426 auto& slot = m_slots[m_slotIdx];
428 m_slotIdx = (m_slotIdx + 1) % NumSlots;
430 slot.frameRef = av_frame_alloc();
431 if(av_frame_ref(slot.frameRef, &frame) < 0)
433 av_frame_free(&slot.frameRef);
434 slot.frameRef =
nullptr;
439 int numSrcImages = 0;
440 for(
int i = 0; i < m_numPlanes; i++)
441 if(vkf->img[i] != VK_NULL_HANDLE)
444 const bool isMultiplane = (numSrcImages == 1 && m_numPlanes > 1);
454 m_dfuncs->vkResetCommandBuffer(m_cmdBuf, 0);
456 VkCommandBufferBeginInfo beginInfo{};
457 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
458 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
459 m_dfuncs->vkBeginCommandBuffer(m_cmdBuf, &beginInfo);
462 VkImageMemoryBarrier barriers[4]{};
463 static const VkImageAspectFlagBits planeAspects[] = {
464 VK_IMAGE_ASPECT_PLANE_0_BIT,
465 VK_IMAGE_ASPECT_PLANE_1_BIT,
466 VK_IMAGE_ASPECT_PLANE_2_BIT,
469 for(
int i = 0; i < m_numPlanes && i < 3; i++)
471 barriers[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
472 barriers[i].srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
473 barriers[i].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
474 barriers[i].oldLayout = vkf->layout[0];
475 barriers[i].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
476 barriers[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
477 barriers[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
478 barriers[i].image = vkf->img[0];
479 barriers[i].subresourceRange.aspectMask = planeAspects[i];
480 barriers[i].subresourceRange.baseMipLevel = 0;
481 barriers[i].subresourceRange.levelCount = 1;
482 barriers[i].subresourceRange.baseArrayLayer = 0;
483 barriers[i].subresourceRange.layerCount = 1;
486 m_dfuncs->vkCmdPipelineBarrier(
488 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
489 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
490 0, 0,
nullptr, 0,
nullptr,
491 static_cast<uint32_t
>(m_numPlanes), barriers);
493 m_dfuncs->vkEndCommandBuffer(m_cmdBuf);
495 m_dfuncs->vkResetFences(m_dev, 1, &m_fence);
496 VkSubmitInfo submitInfo{};
497 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
498 submitInfo.commandBufferCount = 1;
499 submitInfo.pCommandBuffers = &m_cmdBuf;
500 m_dfuncs->vkQueueSubmit(m_gfxQueue, 1, &submitInfo, m_fence);
501 m_dfuncs->vkWaitForFences(m_dev, 1, &m_fence, VK_TRUE, UINT64_MAX);
504 for(
int i = 0; i < m_numPlanes && i < (int)samplers.size(); i++)
506 auto* vkTex =
static_cast<QVkTexture*
>(samplers[i].texture);
508 VkImageViewCreateInfo viewInfo{};
509 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
510 viewInfo.image = vkf->img[0];
511 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
512 viewInfo.format = vkTex->vkformat;
513 viewInfo.components = {
514 VK_COMPONENT_SWIZZLE_IDENTITY,
515 VK_COMPONENT_SWIZZLE_IDENTITY,
516 VK_COMPONENT_SWIZZLE_IDENTITY,
517 VK_COMPONENT_SWIZZLE_IDENTITY};
518 viewInfo.subresourceRange.aspectMask = planeAspects[i];
519 viewInfo.subresourceRange.baseMipLevel = 0;
520 viewInfo.subresourceRange.levelCount = 1;
521 viewInfo.subresourceRange.baseArrayLayer = 0;
522 viewInfo.subresourceRange.layerCount = 1;
524 VkImageView planeView = VK_NULL_HANDLE;
525 if(m_dfuncs->vkCreateImageView(
526 m_dev, &viewInfo,
nullptr, &planeView)
530 slot.planeViews[i] = planeView;
531 slot.numViews = i + 1;
538 if(vkTex->owns && vkTex->imageView != VK_NULL_HANDLE)
539 m_dfuncs->vkDestroyImageView(m_dev, vkTex->imageView,
nullptr);
541 vkTex->image = vkf->img[0];
542 vkTex->imageView = planeView;
545 vkTex->usageState.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
546 vkTex->usageState.access = VK_ACCESS_SHADER_READ_BIT;
547 vkTex->usageState.stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
554 for(
int i = 0; i < m_numPlanes && i < (int)samplers.size(); i++)
556 if(vkf->img[i] != VK_NULL_HANDLE)
558 samplers[i].texture->createFrom(QRhiTexture::NativeTexture{
559 quint64(vkf->img[i]), int(vkf->layout[i])});
567 void createTex(QRhi& rhi, QRhiTexture::Format fmt,
int w,
int h)
569 auto tex = rhi.newTexture(fmt, {w, h}, 1, QRhiTexture::Flag{});
571 auto sampler = rhi.newSampler(
572 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
573 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
575 samplers.push_back({sampler, tex});
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