Loading...
Searching...
No Matches
HWD3D11.hpp
1#pragma once
2#if defined(_WIN32)
3
4#include <Gfx/Graph/decoders/ColorSpace.hpp>
5#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
6#include <Gfx/Graph/decoders/NV12.hpp>
7#include <Gfx/Graph/decoders/P010.hpp>
8#include <Video/GpuFormats.hpp>
9
10#include <QtGui/private/qrhid3d11_p.h>
11
12extern "C" {
13#include <libavformat/avformat.h>
14#if __has_include(<libavutil/hwcontext_d3d11va.h>)
15#include <libavutil/hwcontext_d3d11va.h>
16#define SCORE_HAS_D3D11_HWCONTEXT 1
17#endif
18}
19
20#if defined(SCORE_HAS_D3D11_HWCONTEXT)
21
22#include <d3d11.h>
23
24namespace score::gfx
25{
26
36struct HWD3D11Decoder : GPUVideoDecoder
37{
38 Video::ImageFormat& decoder;
39 PixelFormatInfo m_fmt;
40
41 ID3D11Device* m_dev{};
42 ID3D11DeviceContext* m_ctx{};
43 ID3D11Texture2D* m_nv12Tex{}; // Single NV12/P010 copy target
44 bool m_ready{false};
45
46 static bool isAvailable(QRhi& rhi)
47 {
48 return rhi.backend() == QRhi::D3D11;
49 }
50
51 explicit HWD3D11Decoder(
52 Video::ImageFormat& d, QRhi& rhi, PixelFormatInfo fmt)
53 : decoder{d}
54 , m_fmt{fmt}
55 {
56 auto* nh = static_cast<const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
57 m_dev = static_cast<ID3D11Device*>(nh->dev);
58 m_dev->GetImmediateContext(&m_ctx);
59 }
60
61 ~HWD3D11Decoder() override
62 {
63 if(m_nv12Tex)
64 m_nv12Tex->Release();
65 if(m_ctx)
66 m_ctx->Release();
67 }
68
69 bool setupTextures()
70 {
71 const int w = decoder.width, h = decoder.height;
72 DXGI_FORMAT nv12Fmt = m_fmt.is10bit() ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12;
73
74 D3D11_TEXTURE2D_DESC desc{};
75 desc.Width = w;
76 desc.Height = h;
77 desc.MipLevels = 1;
78 desc.ArraySize = 1;
79 desc.Format = nv12Fmt;
80 desc.SampleDesc.Count = 1;
81 desc.Usage = D3D11_USAGE_DEFAULT;
82 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
83 if(FAILED(m_dev->CreateTexture2D(&desc, nullptr, &m_nv12Tex)))
84 {
85 qDebug() << "HWD3D11Decoder: failed to create NV12 copy texture"
86 << w << "x" << h << "fmt:" << nv12Fmt;
87 return false;
88 }
89
90 m_ready = true;
91 return true;
92 }
93
94 std::pair<QShader, QShader> init(RenderList& r) override
95 {
96 auto& rhi = *r.state.rhi;
97 const int w = decoder.width, h = decoder.height;
98 auto texFmt = m_fmt.is10bit() ? QRhiTexture::R16 : QRhiTexture::R8;
99 auto uvTexFmt = m_fmt.is10bit() ? QRhiTexture::RG16 : QRhiTexture::RG8;
100
101 {
102 auto tex = rhi.newTexture(texFmt, {w, h}, 1, QRhiTexture::Flag{});
103 tex->create();
104 auto sampler = rhi.newSampler(
105 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
106 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
107 sampler->create();
108 samplers.push_back({sampler, tex});
109 }
110 {
111 auto tex = rhi.newTexture(uvTexFmt, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
112 tex->create();
113 auto sampler = rhi.newSampler(
114 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
115 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
116 sampler->create();
117 samplers.push_back({sampler, tex});
118 }
119
120 setupTextures();
121
122 if(m_fmt.is10bit())
124 r.state, vertexShader(),
125 QString(P010Decoder::frag).arg("").arg(colorMatrix(decoder)));
126
127 QString frag = NV12Decoder::nv12_filter_prologue;
128 frag += " vec3 yuv = vec3(y, u, v);\n";
129 frag += NV12Decoder::nv12_filter_epilogue;
131 r.state, vertexShader(), frag.arg("").arg(colorMatrix(decoder)));
132 }
133
134 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame) override
135 {
136 if(!m_ready || !Video::formatIsHardwareDecoded(
137 static_cast<AVPixelFormat>(frame.format)))
138 return;
139
140 // D3D11VA: data[0] = ID3D11Texture2D* (array), data[1] = array index
141 auto* srcTex = reinterpret_cast<ID3D11Texture2D*>(frame.data[0]);
142 auto srcIdx = static_cast<UINT>(reinterpret_cast<intptr_t>(frame.data[1]));
143 if(!srcTex)
144 return;
145
146 const int w = decoder.width, h = decoder.height;
147
148 // Same-format NV12→NV12 copy using the array index as subresource.
149 // This copies both Y and UV planes in one operation (matching FFmpeg's
150 // own transfer pattern in hwcontext_d3d11va.c).
151 D3D11_BOX box = {0, 0, 0, (UINT)w, (UINT)h, 1};
152 m_ctx->CopySubresourceRegion(
153 m_nv12Tex, 0, 0, 0, 0,
154 srcTex, srcIdx, &box);
155
156 // Wrap the NV12 copy texture into QRhi.
157 // D3D11 selects the plane based on the SRV format:
158 // R8/R16 SRV → Y plane (luminance)
159 // R8G8/R16G16 SRV → UV plane (chrominance)
160 samplers[0].texture->createFrom(
161 QRhiTexture::NativeTexture{quint64(m_nv12Tex), 0});
162 samplers[1].texture->createFrom(
163 QRhiTexture::NativeTexture{quint64(m_nv12Tex), 0});
164 }
165};
166
167} // namespace score::gfx
168
169#endif // SCORE_HAS_D3D11_HWCONTEXT
170#endif // _WIN32
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
Definition VideoInterface.hpp:26