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;
60 VkDevice m_dev{VK_NULL_HANDLE};
61 VkPhysicalDevice m_physDev{VK_NULL_HANDLE};
62 QVulkanFunctions* m_funcs{};
63 QVulkanDeviceFunctions* m_dfuncs{};
64 PFN_vkWaitSemaphores m_vkWaitSemaphores{};
65 uint32_t m_gfxQueueFamilyIdx{0};
66 VkQueue m_gfxQueue{VK_NULL_HANDLE};
69 VkCommandPool m_cmdPool{VK_NULL_HANDLE};
70 VkCommandBuffer m_cmdBuf{VK_NULL_HANDLE};
71 VkFence m_fence{VK_NULL_HANDLE};
72 bool m_cmdReady{
false};
75 static constexpr int NumSlots = 3;
79 VkImageView planeViews[4]{};
82 FrameSlot m_slots[NumSlots]{};
87 static bool isAvailable(QRhi& rhi)
89 if(rhi.backend() != QRhi::Vulkan)
92 =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
93 if(!nh || !nh->dev || !nh->physDev || !nh->inst)
95 return nh->inst->getInstanceProcAddr(
"vkWaitSemaphores") !=
nullptr;
98 explicit HWVulkanSharedDecoder(
104 =
static_cast<const QRhiVulkanNativeHandles*
>(rhi.nativeHandles());
106 m_physDev = nh->physDev;
107 m_funcs = nh->inst->functions();
108 m_dfuncs = nh->inst->deviceFunctions(m_dev);
109 m_gfxQueueFamilyIdx = nh->gfxQueueFamilyIdx;
110 m_vkWaitSemaphores =
reinterpret_cast<PFN_vkWaitSemaphores
>(
111 nh->inst->getInstanceProcAddr(
"vkWaitSemaphores"));
112 m_dfuncs->vkGetDeviceQueue(
113 m_dev, m_gfxQueueFamilyIdx, 0, &m_gfxQueue);
116 ~HWVulkanSharedDecoder()
override
123 if(m_gfxQueue != VK_NULL_HANDLE)
124 m_dfuncs->vkQueueWaitIdle(m_gfxQueue);
126 for(
auto& slot : m_slots)
128 if(m_fence != VK_NULL_HANDLE)
129 m_dfuncs->vkDestroyFence(m_dev, m_fence,
nullptr);
130 if(m_cmdPool != VK_NULL_HANDLE)
131 m_dfuncs->vkDestroyCommandPool(m_dev, m_cmdPool,
nullptr);
134 void cleanupSlot(FrameSlot& slot)
136 for(
int i = 0; i < slot.numViews; i++)
138 if(slot.planeViews[i] != VK_NULL_HANDLE)
140 m_dfuncs->vkDestroyImageView(m_dev, slot.planeViews[i],
nullptr);
141 slot.planeViews[i] = VK_NULL_HANDLE;
147 av_frame_free(&slot.frameRef);
148 slot.frameRef =
nullptr;
152 bool setupCommandInfra()
154 VkCommandPoolCreateInfo poolInfo{};
155 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
156 poolInfo.queueFamilyIndex = m_gfxQueueFamilyIdx;
157 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
158 if(m_dfuncs->vkCreateCommandPool(m_dev, &poolInfo,
nullptr, &m_cmdPool)
162 VkCommandBufferAllocateInfo allocInfo{};
163 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
164 allocInfo.commandPool = m_cmdPool;
165 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
166 allocInfo.commandBufferCount = 1;
167 if(m_dfuncs->vkAllocateCommandBuffers(m_dev, &allocInfo, &m_cmdBuf)
171 VkFenceCreateInfo fenceInfo{};
172 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
173 if(m_dfuncs->vkCreateFence(m_dev, &fenceInfo,
nullptr, &m_fence)
185 std::pair<QShader, QShader> init(RenderList& r)
override
187 auto& rhi = *r.state.rhi;
188 const auto w = decoder.width, h = decoder.height;
189 const bool is10 = m_fmt.is10bit();
190 auto texFmt = is10 ? QRhiTexture::R16 : QRhiTexture::R8;
191 int chromaW = AV_CEIL_RSHIFT(w, m_fmt.log2ChromaW);
192 int chromaH = AV_CEIL_RSHIFT(h, m_fmt.log2ChromaH);
199 m_numPlanes = m_fmt.numPlanes;
203 auto uvFmt = is10 ? QRhiTexture::RG16 : QRhiTexture::RG8;
204 createTex(rhi, texFmt, w, h);
205 createTex(rhi, uvFmt, chromaW, chromaH);
209 r.state, vertexShader(),
210 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
213 QString frag = NV12Decoder::nv12_filter_prologue;
214 frag +=
" vec3 yuv = vec3(y, u, v);\n";
215 frag += NV12Decoder::nv12_filter_epilogue;
217 r.state, vertexShader(),
218 frag.arg(
"").arg(colorMatrix(decoder)));
221 else if(m_fmt.hasAlpha)
224 createTex(rhi, texFmt, w, h);
225 createTex(rhi, texFmt, chromaW, chromaH);
226 createTex(rhi, texFmt, chromaW, chromaH);
227 createTex(rhi, texFmt, w, h);
232 r.state, vertexShader(),
233 QString(YUVA444Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
239 double scale = 65535.0 / ((1 << m_fmt.bitDepth) - 1);
240 QString frag = QString(R
"_(#version 450
242)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
244layout(binding=3) uniform sampler2D y_tex;
245layout(binding=4) uniform sampler2D u_tex;
246layout(binding=5) uniform sampler2D v_tex;
247layout(binding=6) uniform sampler2D a_tex;
249layout(location = 0) in vec2 v_texcoord;
250layout(location = 0) out vec4 fragColor;
254vec4 processTexture(vec4 tex) {
255 vec4 processed = convert_to_rgb(tex);
262 float sc = float(%3);
263 float y = sc * texture(y_tex, v_texcoord).r;
264 float u = sc * texture(u_tex, v_texcoord).r;
265 float v = sc * texture(v_tex, v_texcoord).r;
266 float a = sc * texture(a_tex, v_texcoord).r;
268 vec4 rgb = processTexture(vec4(y,u,v, 1.));
269 fragColor = vec4(rgb.rgb, a);
271)_").arg("").arg(colorMatrix(decoder)).arg(scale, 0,
'f', 6);
279 createTex(rhi, texFmt, w, h);
280 createTex(rhi, texFmt, chromaW, chromaH);
281 createTex(rhi, texFmt, chromaW, chromaH);
285 const char* fragSrc = YUV420Decoder::frag;
286 if(m_fmt.log2ChromaW == 1 && m_fmt.log2ChromaH == 0)
287 fragSrc = YUV422Decoder::frag;
288 else if(m_fmt.log2ChromaW == 0 && m_fmt.log2ChromaH == 0)
289 fragSrc = YUV444Decoder::frag;
291 r.state, vertexShader(),
292 QString(fragSrc).arg(
"").arg(colorMatrix(decoder)));
297 double scale = 65535.0 / ((1 << m_fmt.bitDepth) - 1);
298 QString frag = QString(R
"_(#version 450
300)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
302layout(binding=3) uniform sampler2D y_tex;
303layout(binding=4) uniform sampler2D u_tex;
304layout(binding=5) uniform sampler2D v_tex;
306layout(location = 0) in vec2 v_texcoord;
307layout(location = 0) out vec4 fragColor;
311vec4 processTexture(vec4 tex) {
312 vec4 processed = convert_to_rgb(tex);
319 float sc = float(%3);
320 float y = sc * texture(y_tex, v_texcoord).r;
321 float u = sc * texture(u_tex, v_texcoord).r;
322 float v = sc * texture(v_tex, v_texcoord).r;
324 fragColor = processTexture(vec4(y,u,v, 1.));
326)_").arg("").arg(colorMatrix(decoder)).arg(scale, 0,
'f', 6);
337 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
339#if LIBAVUTIL_VERSION_MAJOR >= 57
340 if(!Video::formatIsHardwareDecoded(
341 static_cast<AVPixelFormat
>(frame.format)))
344 auto* vkf =
reinterpret_cast<AVVkFrame*
>(frame.data[0]);
345 if(!vkf || vkf->img[0] == VK_NULL_HANDLE)
348 if(!m_cmdReady && !setupCommandInfra())
352 if(m_vkWaitSemaphores && vkf->sem[0] != VK_NULL_HANDLE)
355 for(
int i = 0; i < 4; i++)
356 if(vkf->sem[i] != VK_NULL_HANDLE)
360 VkSemaphoreWaitInfo waitInfo{};
361 waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
362 waitInfo.semaphoreCount =
static_cast<uint32_t
>(numSems);
363 waitInfo.pSemaphores = vkf->sem;
364 waitInfo.pValues = vkf->sem_value;
365 m_vkWaitSemaphores(m_dev, &waitInfo, UINT64_MAX);
370 auto& slot = m_slots[m_slotIdx];
372 m_slotIdx = (m_slotIdx + 1) % NumSlots;
374 slot.frameRef = av_frame_alloc();
375 if(av_frame_ref(slot.frameRef, &frame) < 0)
377 av_frame_free(&slot.frameRef);
378 slot.frameRef =
nullptr;
383 int numSrcImages = 0;
384 for(
int i = 0; i < m_numPlanes; i++)
385 if(vkf->img[i] != VK_NULL_HANDLE)
388 const bool isMultiplane = (numSrcImages == 1 && m_numPlanes > 1);
398 m_dfuncs->vkResetCommandBuffer(m_cmdBuf, 0);
400 VkCommandBufferBeginInfo beginInfo{};
401 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
402 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
403 m_dfuncs->vkBeginCommandBuffer(m_cmdBuf, &beginInfo);
406 VkImageMemoryBarrier barriers[4]{};
407 static const VkImageAspectFlagBits planeAspects[] = {
408 VK_IMAGE_ASPECT_PLANE_0_BIT,
409 VK_IMAGE_ASPECT_PLANE_1_BIT,
410 VK_IMAGE_ASPECT_PLANE_2_BIT,
413 for(
int i = 0; i < m_numPlanes && i < 3; i++)
415 barriers[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
416 barriers[i].srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
417 barriers[i].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
418 barriers[i].oldLayout = vkf->layout[0];
419 barriers[i].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
420 barriers[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
421 barriers[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
422 barriers[i].image = vkf->img[0];
423 barriers[i].subresourceRange.aspectMask = planeAspects[i];
424 barriers[i].subresourceRange.baseMipLevel = 0;
425 barriers[i].subresourceRange.levelCount = 1;
426 barriers[i].subresourceRange.baseArrayLayer = 0;
427 barriers[i].subresourceRange.layerCount = 1;
430 m_dfuncs->vkCmdPipelineBarrier(
432 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
433 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
434 0, 0,
nullptr, 0,
nullptr,
435 static_cast<uint32_t
>(m_numPlanes), barriers);
437 m_dfuncs->vkEndCommandBuffer(m_cmdBuf);
439 m_dfuncs->vkResetFences(m_dev, 1, &m_fence);
440 VkSubmitInfo submitInfo{};
441 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
442 submitInfo.commandBufferCount = 1;
443 submitInfo.pCommandBuffers = &m_cmdBuf;
444 m_dfuncs->vkQueueSubmit(m_gfxQueue, 1, &submitInfo, m_fence);
445 m_dfuncs->vkWaitForFences(m_dev, 1, &m_fence, VK_TRUE, UINT64_MAX);
448 for(
int i = 0; i < m_numPlanes && i < (int)samplers.size(); i++)
450 auto* vkTex =
static_cast<QVkTexture*
>(samplers[i].texture);
452 VkImageViewCreateInfo viewInfo{};
453 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
454 viewInfo.image = vkf->img[0];
455 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
456 viewInfo.format = vkTex->vkformat;
457 viewInfo.components = {
458 VK_COMPONENT_SWIZZLE_IDENTITY,
459 VK_COMPONENT_SWIZZLE_IDENTITY,
460 VK_COMPONENT_SWIZZLE_IDENTITY,
461 VK_COMPONENT_SWIZZLE_IDENTITY};
462 viewInfo.subresourceRange.aspectMask = planeAspects[i];
463 viewInfo.subresourceRange.baseMipLevel = 0;
464 viewInfo.subresourceRange.levelCount = 1;
465 viewInfo.subresourceRange.baseArrayLayer = 0;
466 viewInfo.subresourceRange.layerCount = 1;
468 VkImageView planeView = VK_NULL_HANDLE;
469 if(m_dfuncs->vkCreateImageView(
470 m_dev, &viewInfo,
nullptr, &planeView)
474 slot.planeViews[i] = planeView;
475 slot.numViews = i + 1;
478 if(vkTex->imageView != VK_NULL_HANDLE)
479 m_dfuncs->vkDestroyImageView(m_dev, vkTex->imageView,
nullptr);
481 vkTex->image = vkf->img[0];
482 vkTex->imageView = planeView;
485 vkTex->usageState.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
486 vkTex->usageState.access = VK_ACCESS_SHADER_READ_BIT;
487 vkTex->usageState.stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
494 for(
int i = 0; i < m_numPlanes && i < (int)samplers.size(); i++)
496 if(vkf->img[i] != VK_NULL_HANDLE)
498 samplers[i].texture->createFrom(QRhiTexture::NativeTexture{
499 quint64(vkf->img[i]), int(vkf->layout[i])});
507 void createTex(QRhi& rhi, QRhiTexture::Format fmt,
int w,
int h)
509 auto tex = rhi.newTexture(fmt, {w, h}, 1, QRhiTexture::Flag{});
511 auto sampler = rhi.newSampler(
512 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
513 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
515 samplers.push_back({sampler, tex});
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:395