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