Loading...
Searching...
No Matches
HWD3D12.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/qrhid3d12_p.h>
11
12extern "C" {
13#include <libavformat/avformat.h>
14#if __has_include(<libavutil/hwcontext_d3d12va.h>)
15#include <libavutil/hwcontext_d3d12va.h>
16#define SCORE_HAS_D3D12_HWCONTEXT 1
17#endif
18}
19
20#if defined(SCORE_HAS_D3D12_HWCONTEXT)
21
22// clang-format off
23#include <windows.h>
24#include <d3d12.h>
25// clang-format on
26
27namespace score::gfx
28{
29
36struct HWD3D12Decoder : GPUVideoDecoder
37{
38 Video::ImageFormat& decoder;
39 PixelFormatInfo m_fmt;
40
41 ID3D12Device* m_dev{};
42 ID3D12CommandQueue* m_cmdQueue{};
43 ID3D12CommandAllocator* m_cmdAlloc{};
44 ID3D12GraphicsCommandList* m_cmdList{};
45 ID3D12Fence* m_fence{};
46 HANDLE m_fenceEvent{};
47 UINT64 m_fenceValue{0};
48
49 ID3D12Resource* m_yTex{};
50 ID3D12Resource* m_uvTex{};
51 bool m_ready{false};
52
53 static bool isAvailable(QRhi& rhi)
54 {
55 return rhi.backend() == QRhi::D3D12;
56 }
57
58 explicit HWD3D12Decoder(
59 Video::ImageFormat& d, QRhi& rhi, PixelFormatInfo fmt)
60 : decoder{d}
61 , m_fmt{fmt}
62 {
63 auto* nh = static_cast<const QRhiD3D12NativeHandles*>(rhi.nativeHandles());
64 m_dev = static_cast<ID3D12Device*>(nh->dev);
65 m_cmdQueue = static_cast<ID3D12CommandQueue*>(nh->commandQueue);
66 }
67
68 ~HWD3D12Decoder() override
69 {
70 if(m_yTex)
71 m_yTex->Release();
72 if(m_uvTex)
73 m_uvTex->Release();
74 if(m_cmdList)
75 m_cmdList->Release();
76 if(m_cmdAlloc)
77 m_cmdAlloc->Release();
78 if(m_fence)
79 m_fence->Release();
80 if(m_fenceEvent)
81 CloseHandle(m_fenceEvent);
82 }
83
84 bool setupTextures()
85 {
86 const int w = decoder.width, h = decoder.height;
87 DXGI_FORMAT yFmt = m_fmt.is10bit() ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM;
88 DXGI_FORMAT uvFmt
89 = m_fmt.is10bit() ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM;
90
91 auto makeTexture = [&](DXGI_FORMAT fmt, UINT tw, UINT th,
92 ID3D12Resource** out) -> bool {
93 D3D12_HEAP_PROPERTIES heap{};
94 heap.Type = D3D12_HEAP_TYPE_DEFAULT;
95
96 D3D12_RESOURCE_DESC desc{};
97 desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
98 desc.Width = tw;
99 desc.Height = th;
100 desc.DepthOrArraySize = 1;
101 desc.MipLevels = 1;
102 desc.Format = fmt;
103 desc.SampleDesc.Count = 1;
104 desc.Flags = D3D12_RESOURCE_FLAG_NONE;
105
106 return SUCCEEDED(m_dev->CreateCommittedResource(
107 &heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
108 nullptr, IID_PPV_ARGS(out)));
109 };
110
111 if(!makeTexture(yFmt, w, h, &m_yTex))
112 return false;
113 if(!makeTexture(uvFmt, w / 2, h / 2, &m_uvTex))
114 return false;
115
116 // Command allocator + list
117 if(FAILED(m_dev->CreateCommandAllocator(
118 D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_cmdAlloc))))
119 return false;
120
121 if(FAILED(m_dev->CreateCommandList(
122 0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_cmdAlloc, nullptr,
123 IID_PPV_ARGS(&m_cmdList))))
124 return false;
125
126 // Close immediately — we reset before each use
127 m_cmdList->Close();
128
129 // Fence for GPU sync
130 if(FAILED(m_dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence))))
131 return false;
132
133 m_fenceEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
134 if(!m_fenceEvent)
135 return false;
136
137 m_ready = true;
138 return true;
139 }
140
141 std::pair<QShader, QShader> init(RenderList& r) override
142 {
143 auto& rhi = *r.state.rhi;
144 const int w = decoder.width, h = decoder.height;
145 auto texFmt = m_fmt.is10bit() ? QRhiTexture::R16 : QRhiTexture::R8;
146 auto uvTexFmt = m_fmt.is10bit() ? QRhiTexture::RG16 : QRhiTexture::RG8;
147
148 {
149 auto tex = rhi.newTexture(texFmt, {w, h}, 1, QRhiTexture::Flag{});
150 tex->create();
151 auto sampler = rhi.newSampler(
152 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
153 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
154 sampler->create();
155 samplers.push_back({sampler, tex});
156 }
157 {
158 auto tex = rhi.newTexture(uvTexFmt, {w / 2, h / 2}, 1, QRhiTexture::Flag{});
159 tex->create();
160 auto sampler = rhi.newSampler(
161 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
162 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
163 sampler->create();
164 samplers.push_back({sampler, tex});
165 }
166
167 setupTextures();
168
169 if(m_fmt.is10bit())
171 r.state, vertexShader(),
172 QString(P010Decoder::frag).arg("").arg(colorMatrix(decoder)));
173
174 QString frag = NV12Decoder::nv12_filter_prologue;
175 frag += " vec3 yuv = vec3(y, u, v);\n";
176 frag += NV12Decoder::nv12_filter_epilogue;
178 r.state, vertexShader(), frag.arg("").arg(colorMatrix(decoder)));
179 }
180
181 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame) override
182 {
183 if(!m_ready || !Video::formatIsHardwareDecoded(
184 static_cast<AVPixelFormat>(frame.format)))
185 return;
186
187 // D3D12VA: data[0] = AVD3D12VAFrame*
188 auto* d3d12Frame = reinterpret_cast<AVD3D12VAFrame*>(frame.data[0]);
189 if(!d3d12Frame || !d3d12Frame->texture)
190 return;
191
192 ID3D12Resource* srcTex = d3d12Frame->texture;
193 const int w = decoder.width, h = decoder.height;
194
195 // Wait for FFmpeg's decode to complete
196 auto& sync = d3d12Frame->sync_ctx;
197 if(sync.fence && sync.fence_value > 0)
198 {
199 if(sync.fence->GetCompletedValue() < sync.fence_value)
200 {
201 sync.fence->SetEventOnCompletion(sync.fence_value, m_fenceEvent);
202 WaitForSingleObject(m_fenceEvent, INFINITE);
203 }
204 }
205
206 // Reset and record copy commands
207 m_cmdAlloc->Reset();
208 m_cmdList->Reset(m_cmdAlloc, nullptr);
209
210 // Barriers: dst textures COMMON → COPY_DEST
211 D3D12_RESOURCE_BARRIER barriers[2]{};
212 barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
213 barriers[0].Transition.pResource = m_yTex;
214 barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
215 barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
216 barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
217
218 barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
219 barriers[1].Transition.pResource = m_uvTex;
220 barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
221 barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
222 barriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
223
224 m_cmdList->ResourceBarrier(2, barriers);
225
226 // Copy Y plane (subresource 0 of NV12/P010 texture)
227 {
228 D3D12_TEXTURE_COPY_LOCATION dst{};
229 dst.pResource = m_yTex;
230 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
231 dst.SubresourceIndex = 0;
232
233 D3D12_TEXTURE_COPY_LOCATION src{};
234 src.pResource = srcTex;
235 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
236 src.SubresourceIndex = 0;
237
238 D3D12_BOX box = {0, 0, 0, (UINT)w, (UINT)h, 1};
239 m_cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, &box);
240 }
241
242 // Copy UV plane (subresource 1 of NV12/P010 texture)
243 {
244 D3D12_TEXTURE_COPY_LOCATION dst{};
245 dst.pResource = m_uvTex;
246 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
247 dst.SubresourceIndex = 0;
248
249 D3D12_TEXTURE_COPY_LOCATION src{};
250 src.pResource = srcTex;
251 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
252 src.SubresourceIndex = 1;
253
254 D3D12_BOX box = {0, 0, 0, (UINT)(w / 2), (UINT)(h / 2), 1};
255 m_cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, &box);
256 }
257
258 // Barriers: dst textures COPY_DEST → COMMON
259 barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
260 barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
261 barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
262 barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
263
264 m_cmdList->ResourceBarrier(2, barriers);
265 m_cmdList->Close();
266
267 // Execute and wait
268 ID3D12CommandList* lists[] = {m_cmdList};
269 m_cmdQueue->ExecuteCommandLists(1, lists);
270
271 m_fenceValue++;
272 m_cmdQueue->Signal(m_fence, m_fenceValue);
273 if(m_fence->GetCompletedValue() < m_fenceValue)
274 {
275 m_fence->SetEventOnCompletion(m_fenceValue, m_fenceEvent);
276 WaitForSingleObject(m_fenceEvent, INFINITE);
277 }
278
279 // Wrap our standalone textures into QRhi
280 samplers[0].texture->createFrom(
281 QRhiTexture::NativeTexture{quint64(m_yTex), 0});
282 samplers[1].texture->createFrom(
283 QRhiTexture::NativeTexture{quint64(m_uvTex), 0});
284 }
285};
286
287} // namespace score::gfx
288
289#endif // SCORE_HAS_D3D12_HWCONTEXT
290#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