Loading...
Searching...
No Matches
DvpOutputD3D11.hpp
1#pragma once
3#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
4#include <nv_dvp_bridge.h>
5
6#include <QtGui/private/qrhid3d11_p.h>
7
8#include <QDebug>
9
10#include <d3d11.h>
11
12#include <cstdint>
13#include <cstring>
14#include <functional>
15#include <memory>
16
17namespace score::gfx::interop
18{
19
27template <class Lock>
29{
30 using MakeEncoder = std::function<std::unique_ptr<score::gfx::GPUVideoEncoder>()>;
31 using GetOutputTexture
32 = std::function<QRhiTexture*(score::gfx::GPUVideoEncoder*)>;
33
35 Lock lock, MakeEncoder makeEncoder, GetOutputTexture getOutputTexture,
36 const char* name) noexcept
37 : m_lock{lock}
38 , m_makeEncoder{std::move(makeEncoder)}
39 , m_getOutputTexture{std::move(getOutputTexture)}
40 , m_name{name}
41 {
42 }
43
44 Lock m_lock;
45 MakeEncoder m_makeEncoder;
46 GetOutputTexture m_getOutputTexture;
47 const char* m_name{"DVP-D3D11"};
48
50
51 ID3D11Device* m_dev{};
52
53 NvDvpContextHandle m_dvpCtx{};
54 NvDvpResourceHandle m_dvpTex{};
55 NvDvpResourceHandle m_dvpBuf{};
56 bool m_threadStarted{};
57
58 std::unique_ptr<score::gfx::GPUVideoEncoder> m_encoder;
59 QRhiTexture* m_encoderOutput{}; /* non-owning; encoder owns it */
60
61 void* m_sysmem{};
62 uint32_t m_sysmemBytes{};
63 uint32_t m_sysmemStrideBytes{};
64 uint32_t m_sysmemRows{};
65 bool m_dmaLocked{};
66
67 const char* name() const noexcept override { return m_name; }
68
69 static bool isSupported(QRhi* rhi) { return rhi != nullptr; }
70
71 bool init(const score::gfx::interop::VideoOutputStrategyConfig& c) override
72 {
73 cfg = c;
74 if(!isSupported(cfg.rhi) || !cfg.state || !m_lock.valid())
75 return false;
76
77 auto* native
78 = static_cast<const QRhiD3D11NativeHandles*>(cfg.rhi->nativeHandles());
79 if(!native || !native->dev)
80 return false;
81 m_dev = static_cast<ID3D11Device*>(native->dev);
82
83 qDebug() << "DVP(D3D11): loading dvp.dll...";
84 if(nv_dvp_init_d3d11(&m_dvpCtx, m_dev) != NV_DVP_SUCCESS || !m_dvpCtx)
85 {
86 qWarning() << "DVP(D3D11): init failed:"
87 << nv_dvp_get_error_string(m_dvpCtx);
88 return false;
89 }
90 qDebug() << "DVP(D3D11): dvp.dll loaded + D3D11 device bound";
91
92 if(nv_dvp_thread_begin(m_dvpCtx) != NV_DVP_SUCCESS)
93 {
94 qWarning() << "DVP(D3D11): thread_begin failed:"
95 << nv_dvp_get_error_string(m_dvpCtx);
96 nv_dvp_shutdown(m_dvpCtx);
97 m_dvpCtx = nullptr;
98 return false;
99 }
100 m_threadStarted = true;
101
102 auto colorShader = score::gfx::colorMatrixOut(
103 AVCOL_SPC_BT709, AVCOL_TRC_BT709, AVCOL_RANGE_MPEG, AVCOL_PRI_BT709);
104
105 m_encoder = m_makeEncoder();
106 if(!m_encoder)
107 {
108 qWarning() << "DVP(D3D11): no fragment encoder for format";
109 release();
110 return false;
111 }
112 m_encoder->init(
113 *cfg.rhi, *cfg.state, cfg.sourceTexture, cfg.width, cfg.height,
114 colorShader);
115 m_encoderOutput = m_getOutputTexture(m_encoder.get());
116 if(!m_encoderOutput)
117 {
118 qWarning() << "DVP(D3D11): encoder did not produce an output texture";
119 release();
120 return false;
121 }
122
123 const QSize texSize = m_encoderOutput->pixelSize();
124 m_sysmemRows = uint32_t(texSize.height());
125 m_sysmemStrideBytes = uint32_t(texSize.width()) * 4u;
126 m_sysmemBytes = m_sysmemStrideBytes * m_sysmemRows;
127
128 if(m_sysmemBytes != cfg.frameByteSize)
129 {
130 qWarning() << "DVP(D3D11): encoder output size" << m_sysmemBytes
131 << "does not match frame size" << cfg.frameByteSize
132 << "- aborting";
133 release();
134 return false;
135 }
136
137 m_sysmem = nv_dvp_aligned_alloc(m_sysmemBytes);
138 if(!m_sysmem)
139 {
140 qWarning() << "DVP(D3D11): nv_dvp_aligned_alloc(" << m_sysmemBytes
141 << ") failed";
142 release();
143 return false;
144 }
145 std::memset(m_sysmem, 0, m_sysmemBytes);
146
147 if(!m_lock.lock(m_sysmem, m_sysmemBytes))
148 {
149 qWarning() << "DVP(D3D11): DMABufferLock(sysmem) failed";
150 release();
151 return false;
152 }
153 m_dmaLocked = true;
154
155 auto nt = m_encoderOutput->nativeTexture();
156 if(!nt.object)
157 {
158 qWarning() << "DVP(D3D11): encoder texture has no native handle";
159 release();
160 return false;
161 }
162 auto* d3d11Tex = reinterpret_cast<ID3D11Texture2D*>(nt.object);
163
164 if(nv_dvp_register_d3d11_texture(
165 m_dvpCtx, d3d11Tex, NV_DVP_FORMAT_RGBA8,
166 uint32_t(texSize.width()), uint32_t(texSize.height()), &m_dvpTex)
167 != NV_DVP_SUCCESS)
168 {
169 qWarning() << "DVP(D3D11): register_d3d11_texture failed:"
170 << nv_dvp_get_error_string(m_dvpCtx);
171 release();
172 return false;
173 }
174
175 if(nv_dvp_register_sysmem_buffer(
176 m_dvpCtx, m_sysmem, NV_DVP_FORMAT_RGBA8, uint32_t(texSize.width()),
177 uint32_t(texSize.height()), m_sysmemStrideBytes, &m_dvpBuf)
178 != NV_DVP_SUCCESS)
179 {
180 qWarning() << "DVP(D3D11): register_sysmem_buffer failed:"
181 << nv_dvp_get_error_string(m_dvpCtx);
182 release();
183 return false;
184 }
185
186 return true;
187 }
188
189 void release() override
190 {
191 if(m_dvpCtx)
192 {
193 if(m_dvpTex)
194 {
195 nv_dvp_unregister(m_dvpCtx, m_dvpTex);
196 m_dvpTex = nullptr;
197 }
198 if(m_dvpBuf)
199 {
200 nv_dvp_unregister(m_dvpCtx, m_dvpBuf);
201 m_dvpBuf = nullptr;
202 }
203 if(m_threadStarted)
204 {
205 nv_dvp_thread_end(m_dvpCtx);
206 m_threadStarted = false;
207 }
208 nv_dvp_shutdown(m_dvpCtx);
209 m_dvpCtx = nullptr;
210 }
211 if(m_encoder)
212 {
213 m_encoder->release();
214 m_encoder.reset();
215 }
216 m_encoderOutput = nullptr;
217
218 if(m_dmaLocked && m_sysmem)
219 {
220 m_lock.unlock(m_sysmem, m_sysmemBytes);
221 m_dmaLocked = false;
222 }
223 if(m_sysmem)
224 {
225 nv_dvp_aligned_free(m_sysmem);
226 m_sysmem = nullptr;
227 }
228 m_dev = nullptr;
229 }
230
231 void encodeFrame(QRhiCommandBuffer& cb) override
232 {
233 if(nv_dvp_acquire_texture(m_dvpCtx, m_dvpTex) != NV_DVP_SUCCESS)
234 {
235 qWarning() << "DVP(D3D11): acquire_texture failed:"
236 << nv_dvp_get_error_string(m_dvpCtx);
237 }
238 m_encoder->exec(*cfg.rhi, cb);
239 }
240
241 void* prepareNextFrame() override
242 {
243 if(nv_dvp_release_texture(m_dvpCtx, m_dvpTex) != NV_DVP_SUCCESS)
244 {
245 qWarning() << "DVP(D3D11): release_texture failed:"
246 << nv_dvp_get_error_string(m_dvpCtx);
247 return nullptr;
248 }
249 if(nv_dvp_copy_texture_to_buffer(m_dvpCtx, m_dvpTex, m_dvpBuf)
250 != NV_DVP_SUCCESS)
251 {
252 qWarning() << "DVP(D3D11): copy_texture_to_buffer failed:"
253 << nv_dvp_get_error_string(m_dvpCtx);
254 return nullptr;
255 }
256 return m_sysmem;
257 }
258};
259
260} // namespace score::gfx::interop
Vendor-neutral interface for GPU-direct video OUTPUT strategies.
Base class for GPU-side video format conversion (RGBA to YUV).
Definition GPUVideoEncoder.hpp:30
D3D11 output strategy via NVIDIA "GPUDirect for Video" (DVP).
Definition DvpOutputD3D11.hpp:29
void * prepareNextFrame() override
Definition DvpOutputD3D11.hpp:241
const char * name() const noexcept override
Definition DvpOutputD3D11.hpp:67
void encodeFrame(QRhiCommandBuffer &cb) override
Definition DvpOutputD3D11.hpp:231
Generic config passed to VideoOutputStrategy::init. Vendor- specific bits (card handle,...
Definition VideoOutputStrategy.hpp:59
std::uint32_t frameByteSize
Definition VideoOutputStrategy.hpp:79
const score::gfx::RenderState * state
Definition VideoOutputStrategy.hpp:65
QRhiTexture * sourceTexture
Definition VideoOutputStrategy.hpp:70
Per-graphics-API strategy interface for vendor GPU-direct output. Each addon implements one impl per ...
Definition VideoOutputStrategy.hpp:97