4#include <Gfx/Graph/decoders/ColorSpace.hpp>
5#include <Gfx/Graph/decoders/DMABufImport.hpp>
6#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
7#include <Gfx/Graph/decoders/NV12.hpp>
8#include <Gfx/Graph/decoders/P010.hpp>
9#include <Video/GpuFormats.hpp>
10#include <score/gfx/Vulkan.hpp>
13#include <libavformat/avformat.h>
14#include <libavutil/pixdesc.h>
15#if __has_include(<libavutil/hwcontext.h>)
16#include <libavutil/hwcontext.h>
18#if __has_include(<libavutil/hwcontext_drm.h>)
19#include <libavutil/hwcontext_drm.h>
20#define SCORE_HAS_DRM_HWCONTEXT 1
24#if QT_HAS_VULKAN && defined(SCORE_HAS_DRM_HWCONTEXT) && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
25#include <QtGui/private/qrhivulkan_p.h>
26#include <qvulkanfunctions.h>
28#include <vulkan/vulkan.h>
29#include <vulkan/vulkan.hpp>
34#if defined(VK_EXT_image_drm_format_modifier) && defined(VK_KHR_external_memory_fd)
53struct HWVaapiVulkanDecoder : GPUVideoDecoder
56 PixelFormatInfo m_fmt;
57 DMABufPlaneImporter m_importer;
59 using PlaneImport = DMABufPlaneImporter::PlaneImport;
62 static constexpr int NumSlots = 2;
65 PlaneImport planes[2]{};
68 ImportSlot m_slots[NumSlots]{};
73 static bool isAvailable(QRhi& rhi)
75 return DMABufPlaneImporter::isAvailable(rhi);
78 explicit HWVaapiVulkanDecoder(
86 ~HWVaapiVulkanDecoder()
override
88 for(
auto& slot : m_slots)
92 void cleanupSlot(ImportSlot& slot)
94 for(
auto& p : slot.planes)
95 m_importer.cleanupPlane(p);
98 av_frame_free(&slot.vaapiRef);
99 slot.vaapiRef =
nullptr;
107 std::pair<QShader, QShader> init(RenderList& r)
override
109 auto& rhi = *r.state.rhi;
110 const auto w = decoder.width, h = decoder.height;
116 auto tex = rhi.newTexture(QRhiTexture::R16, {w, h}, 1, QRhiTexture::Flag{});
118 auto sampler = rhi.newSampler(
119 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
120 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
122 samplers.push_back({sampler, tex});
126 = rhi.newTexture(QRhiTexture::RG16, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
128 auto sampler = rhi.newSampler(
129 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
130 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
132 samplers.push_back({sampler, tex});
135 r.state, vertexShader(),
136 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
142 auto tex = rhi.newTexture(QRhiTexture::R8, {w, h}, 1, QRhiTexture::Flag{});
144 auto sampler = rhi.newSampler(
145 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
146 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
148 samplers.push_back({sampler, tex});
152 = rhi.newTexture(QRhiTexture::RG8, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
154 auto sampler = rhi.newSampler(
155 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
156 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
158 samplers.push_back({sampler, tex});
161 QString frag = NV12Decoder::nv12_filter_prologue;
162 frag +=
" vec3 yuv = vec3(y, u, v);\n";
163 frag += NV12Decoder::nv12_filter_epilogue;
165 r.state, vertexShader(), frag.arg(
"").arg(colorMatrix(decoder)));
173 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
175#if LIBAVUTIL_VERSION_MAJOR >= 57
176 if(!Video::formatIsHardwareDecoded(
static_cast<AVPixelFormat
>(frame.format)))
183 auto& slot = m_slots[m_slotIdx];
185 m_slotIdx = (m_slotIdx + 1) % NumSlots;
188 slot.vaapiRef = av_frame_alloc();
189 if(av_frame_ref(slot.vaapiRef, &frame) < 0)
191 av_frame_free(&slot.vaapiRef);
192 slot.vaapiRef =
nullptr;
197 AVFrame* drmFrame = av_frame_alloc();
198 drmFrame->format = AV_PIX_FMT_DRM_PRIME;
200 int ret = av_hwframe_map(drmFrame, &frame,
201 AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_DIRECT);
204 qDebug() <<
"HWVaapiVulkanDecoder: av_hwframe_map failed:" << ret;
205 av_frame_free(&drmFrame);
209 auto* desc =
reinterpret_cast<AVDRMFrameDescriptor*
>(drmFrame->data[0]);
210 if(!desc || desc->nb_objects < 1)
212 av_frame_unref(drmFrame);
213 av_frame_free(&drmFrame);
229 PlaneInfo planeInfo[2]{};
230 bool planesOk =
false;
231 const int w = decoder.width;
232 const int h = decoder.height;
234 const VkFormat yFmt = m_fmt.is10bit() ? VK_FORMAT_R16_UNORM : VK_FORMAT_R8_UNORM;
235 const VkFormat uvFmt = m_fmt.is10bit() ? VK_FORMAT_R16G16_UNORM : VK_FORMAT_R8G8_UNORM;
237 if(desc->nb_layers >= 2
238 && desc->layers[0].nb_planes >= 1
239 && desc->layers[1].nb_planes >= 1)
242 auto& yP = desc->layers[0].planes[0];
243 auto& uvP = desc->layers[1].planes[0];
244 planeInfo[0] = {yP.object_index, yP.offset, yP.pitch, yFmt, w, h};
245 planeInfo[1] = {uvP.object_index, uvP.offset, uvP.pitch, uvFmt, w / 2, h / 2};
248 else if(desc->nb_layers >= 1 && desc->layers[0].nb_planes >= 2)
251 auto& yP = desc->layers[0].planes[0];
252 auto& uvP = desc->layers[0].planes[1];
253 planeInfo[0] = {yP.object_index, yP.offset, yP.pitch, yFmt, w, h};
254 planeInfo[1] = {uvP.object_index, uvP.offset, uvP.pitch, uvFmt, w / 2, h / 2};
260 qDebug() <<
"HWVaapiVulkanDecoder: unexpected DRM layout, layers:"
262 av_frame_unref(drmFrame);
263 av_frame_free(&drmFrame);
268 for(
int i = 0; i < 2; ++i)
270 auto& pi = planeInfo[i];
271 auto& obj = desc->objects[pi.obj_idx];
272 if(!m_importer.importPlane(slot.planes[i], obj.fd, obj.format_modifier,
273 pi.offset, pi.pitch, pi.format, pi.w, pi.h))
275 qDebug() <<
"HWVaapiVulkanDecoder: importPlane failed, plane" << i;
276 av_frame_unref(drmFrame);
277 av_frame_free(&drmFrame);
284 av_frame_unref(drmFrame);
285 av_frame_free(&drmFrame);
288 for(
int i = 0; i < 2; ++i)
290 samplers[i].texture->createFrom(
291 QRhiTexture::NativeTexture{
292 quint64(slot.planes[i].image),
293 VK_IMAGE_LAYOUT_UNDEFINED});
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