4#include <Gfx/Graph/decoders/ColorSpace.hpp>
5#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
6#include <Gfx/Graph/decoders/HWVideoToolbox_metal.hpp>
7#include <Gfx/Graph/decoders/NV12.hpp>
8#include <Gfx/Graph/decoders/P010.hpp>
9#include <Video/GpuFormats.hpp>
11#include <QtGui/private/qrhi_p.h>
12#include <QtGui/private/qrhimetal_p.h>
15#include <libavformat/avformat.h>
16#include <libavutil/pixdesc.h>
17#if __has_include(<libavutil/hwcontext_videotoolbox.h>)
18#include <libavutil/hwcontext_videotoolbox.h>
19#define SCORE_HAS_VTB_HWCONTEXT 1
23#if defined(SCORE_HAS_VTB_HWCONTEXT)
31 ScoreMetalPixelFormatR8Unorm = 10,
32 ScoreMetalPixelFormatR16Unorm = 20,
33 ScoreMetalPixelFormatRG8Unorm = 30,
34 ScoreMetalPixelFormatRG16Unorm = 60,
35 ScoreMetalPixelFormatRGBA8Unorm = 70,
36 ScoreMetalPixelFormatRGBA16Unorm = 110,
42static const constexpr auto ayuv_frag = R
"_(#version 450
44)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
46layout(binding=3) uniform sampler2D ayuv_tex;
48layout(location = 0) in vec2 v_texcoord;
49layout(location = 0) out vec4 fragColor;
53vec4 processTexture(vec4 tex) {
54 vec4 processed = convert_to_rgb(tex);
61 vec4 tex = texture(ayuv_tex, v_texcoord);
62 float y = tex.g; // Y is in green channel
63 float u = tex.b; // Cb is in blue channel
64 float v = tex.a; // Cr is in alpha channel
65 float a = tex.r; // Alpha is in red channel
67 vec4 rgb = processTexture(vec4(y, u, v, 1.));
68 fragColor = vec4(rgb.rgb, a);
77struct HWVideoToolboxDecoder : GPUVideoDecoder
80 PixelFormatInfo m_fmt;
81 void* m_textureCache{};
87 void* m_retainedCvTexY{};
88 void* m_retainedCvTexUV{};
90 static bool isAvailable(QRhi& rhi)
92 return rhi.backend() == QRhi::Metal;
95 explicit HWVideoToolboxDecoder(
100 auto* nh =
static_cast<const QRhiMetalNativeHandles*
>(rhi.nativeHandles());
102 m_textureCache = createMetalTextureCache(nh->dev);
105 ~HWVideoToolboxDecoder()
override
107 releaseRetainedTextures();
110 releaseMetalTextureCache(m_textureCache);
111 m_textureCache =
nullptr;
115 void releaseRetainedTextures()
119 releaseMetalTextureRef(m_retainedCvTexY);
120 m_retainedCvTexY =
nullptr;
122 if(m_retainedCvTexUV)
124 releaseMetalTextureRef(m_retainedCvTexUV);
125 m_retainedCvTexUV =
nullptr;
129 std::pair<QShader, QShader> init(RenderList& r)
override
131 auto& rhi = *r.state.rhi;
132 const int w = decoder.width, h = decoder.height;
140 auto qrhiFmt = m_fmt.is10bit() ? QRhiTexture::RGBA16F : QRhiTexture::RGBA8;
141 auto tex = rhi.newTexture(qrhiFmt, {w, h}, 1, QRhiTexture::Flag{});
143 auto sampler = rhi.newSampler(
144 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
145 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
147 samplers.push_back({sampler, tex});
150 r.state, vertexShader(),
151 QString(ayuv_frag).arg(
"").arg(colorMatrix(decoder)));
155 auto texFmt = m_fmt.is10bit() ? QRhiTexture::R16 : QRhiTexture::R8;
156 auto uvFmt = m_fmt.is10bit() ? QRhiTexture::RG16 : QRhiTexture::RG8;
157 const int uvW = w >> m_fmt.log2ChromaW;
158 const int uvH = h >> m_fmt.log2ChromaH;
161 auto tex = rhi.newTexture(texFmt, {w, h}, 1, QRhiTexture::Flag{});
163 auto sampler = rhi.newSampler(
164 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
165 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
167 samplers.push_back({sampler, tex});
170 auto tex = rhi.newTexture(uvFmt, {uvW, uvH}, 1, QRhiTexture::Flag{});
172 auto sampler = rhi.newSampler(
173 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
174 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
176 samplers.push_back({sampler, tex});
182 r.state, vertexShader(),
183 QString(P010Decoder::frag).arg(
"").arg(colorMatrix(decoder)));
186 QString frag = NV12Decoder::nv12_filter_prologue;
187 frag +=
" vec3 yuv = vec3(y, u, v);\n";
188 frag += NV12Decoder::nv12_filter_epilogue;
190 r.state, vertexShader(), frag.arg(
"").arg(colorMatrix(decoder)));
193 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame)
override
200 if(!Video::formatIsHardwareDecoded(
static_cast<AVPixelFormat
>(frame.format)))
206 auto pixbuf = (CVPixelBufferRef)frame.data[3];
213 releaseRetainedTextures();
217 execPackedAYUV(pixbuf);
221 execSemiPlanar(pixbuf);
225 void execPackedAYUV(CVPixelBufferRef pixbuf)
229 unsigned mtlFmt = m_fmt.is10bit()
230 ? ScoreMetalPixelFormatRGBA16Unorm
231 : ScoreMetalPixelFormatRGBA8Unorm;
233 auto tex = createMetalTextureFromPixelBuffer(
234 m_textureCache, pixbuf, 0, mtlFmt);
241 QSize sz(tex.width, tex.height);
242 if(samplers[0].texture->pixelSize() != sz)
243 samplers[0].texture->setPixelSize(sz);
244 samplers[0].texture->createFrom(
245 QRhiTexture::NativeTexture{quint64(tex.mtlTexture), 0});
246 m_retainedCvTexY = tex.cvMetalTexture;
249 void execSemiPlanar(CVPixelBufferRef pixbuf)
251 if(getPixelBufferPlaneCount(pixbuf) < 2)
257 unsigned yFmt = m_fmt.is10bit() ? ScoreMetalPixelFormatR16Unorm : ScoreMetalPixelFormatR8Unorm;
258 unsigned uvFmt = m_fmt.is10bit() ? ScoreMetalPixelFormatRG16Unorm : ScoreMetalPixelFormatRG8Unorm;
261 auto yTex = createMetalTextureFromPixelBuffer(m_textureCache, pixbuf, 0, yFmt);
268 QSize ySize(yTex.width, yTex.height);
269 if(samplers[0].texture->pixelSize() != ySize)
270 samplers[0].texture->setPixelSize(ySize);
271 samplers[0].texture->createFrom(
272 QRhiTexture::NativeTexture{quint64(yTex.mtlTexture), 0});
273 m_retainedCvTexY = yTex.cvMetalTexture;
277 auto uvTex = createMetalTextureFromPixelBuffer(m_textureCache, pixbuf, 1, uvFmt);
278 if(!uvTex.mtlTexture)
284 QSize uvSize(uvTex.width, uvTex.height);
285 if(samplers[1].texture->pixelSize() != uvSize)
286 samplers[1].texture->setPixelSize(uvSize);
287 samplers[1].texture->createFrom(
288 QRhiTexture::NativeTexture{quint64(uvTex.mtlTexture), 0});
289 m_retainedCvTexUV = uvTex.cvMetalTexture;
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