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