1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "d3d12_context.h"
25 #include "d3d12_screen.h"
26 #include "d3d12_video_proc.h"
27 #include "d3d12_residency.h"
28 #include "d3d12_util.h"
29 #include "d3d12_resource.h"
30 #include "d3d12_video_buffer.h"
31
32 void
d3d12_video_processor_begin_frame(struct pipe_video_codec * codec,struct pipe_video_buffer * target,struct pipe_picture_desc * picture)33 d3d12_video_processor_begin_frame(struct pipe_video_codec * codec,
34 struct pipe_video_buffer *target,
35 struct pipe_picture_desc *picture)
36 {
37 struct d3d12_video_processor * pD3D12Proc = (struct d3d12_video_processor *) codec;
38 debug_printf("[d3d12_video_processor] d3d12_video_processor_begin_frame - "
39 "fenceValue: %d\n",
40 pD3D12Proc->m_fenceValue);
41
42 // Setup process frame arguments for output/target texture.
43 struct d3d12_video_buffer *pOutputVideoBuffer = (struct d3d12_video_buffer *) target;
44
45 // Make the resources permanently resident for video use
46 d3d12_promote_to_permanent_residency(pD3D12Proc->m_pD3D12Screen, pOutputVideoBuffer->texture);
47
48 ID3D12Resource *pDstD3D12Res = d3d12_resource_resource(pOutputVideoBuffer->texture);
49 auto dstDesc = GetDesc(pDstD3D12Res);
50 pD3D12Proc->m_OutputArguments = {
51 {
52 {
53 pDstD3D12Res, // ID3D12Resource *pTexture2D;
54 0, // UINT Subresource;
55 },
56 {
57 NULL, // ID3D12Resource *pTexture2D;
58 0 // UINT Subresource;
59 }
60 },
61 { 0, 0, (int) dstDesc.Width, (int) dstDesc.Height }
62 };
63
64 debug_printf("d3d12_video_processor_begin_frame: Beginning new scene with Output ID3D12Resource: %p (%d %d)\n", pDstD3D12Res, (int) dstDesc.Width, (int) dstDesc.Height);
65 }
66
67 void
d3d12_video_processor_end_frame(struct pipe_video_codec * codec,struct pipe_video_buffer * target,struct pipe_picture_desc * picture)68 d3d12_video_processor_end_frame(struct pipe_video_codec * codec,
69 struct pipe_video_buffer *target,
70 struct pipe_picture_desc *picture)
71 {
72 struct d3d12_video_processor * pD3D12Proc = (struct d3d12_video_processor *) codec;
73 debug_printf("[d3d12_video_processor] d3d12_video_processor_end_frame - "
74 "fenceValue: %d\n",
75 pD3D12Proc->m_fenceValue);
76
77 auto curOutputDesc = GetOutputStreamDesc(pD3D12Proc->m_spVideoProcessor.Get());
78 auto curOutputTexFmt = GetDesc(pD3D12Proc->m_OutputArguments.OutputStream[0].pTexture2D).Format;
79
80 bool inputFmtsMatch = pD3D12Proc->m_inputStreamDescs.size() == pD3D12Proc->m_ProcessInputs.size();
81 unsigned curInputIdx = 0;
82 while( (curInputIdx < pD3D12Proc->m_inputStreamDescs.size()) && inputFmtsMatch)
83 {
84 inputFmtsMatch = inputFmtsMatch && (pD3D12Proc->m_inputStreamDescs[curInputIdx].Format == GetDesc(pD3D12Proc->m_ProcessInputs[curInputIdx].InputStream[0].pTexture2D).Format);
85 curInputIdx++;
86 }
87
88 bool inputCountMatches = (pD3D12Proc->m_ProcessInputs.size() == pD3D12Proc->m_spVideoProcessor->GetNumInputStreamDescs());
89 bool outputFmtMatches = (curOutputDesc.Format == curOutputTexFmt);
90 bool needsVPRecreation = (
91 !inputCountMatches // Requested batch has different number of Inputs to be blit'd
92 || !outputFmtMatches // output texture format different than vid proc object expects
93 || !inputFmtsMatch // inputs texture formats different than vid proc object expects
94 );
95
96 if(needsVPRecreation) {
97 debug_printf("[d3d12_video_processor] d3d12_video_processor_end_frame - Attempting to re-create ID3D12VideoProcessor "
98 "input count matches %d inputFmtsMatch: %d outputFmtsMatch %d \n", inputCountMatches, inputFmtsMatch, outputFmtMatches);
99
100 DXGI_COLOR_SPACE_TYPE InputColorSpace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
101 DXGI_FORMAT OutputFormat = curOutputTexFmt;
102 DXGI_COLOR_SPACE_TYPE OutputColorSpace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
103
104 std::vector<DXGI_FORMAT> InputFormats;
105 for(D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS1 curInput : pD3D12Proc->m_ProcessInputs)
106 {
107 InputFormats.push_back(GetDesc(curInput.InputStream[0].pTexture2D).Format);
108 }
109
110 if(!d3d12_video_processor_check_caps_and_create_processor(pD3D12Proc, InputFormats, InputColorSpace, OutputFormat, OutputColorSpace))
111 {
112 debug_printf("[d3d12_video_processor] d3d12_video_processor_end_frame - Failure when "
113 " trying to re-create the ID3D12VideoProcessor for current batch streams configuration\n");
114 assert(false);
115 }
116 }
117
118 // Schedule barrier transitions
119 std::vector<D3D12_RESOURCE_BARRIER> barrier_transitions;
120 barrier_transitions.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
121 pD3D12Proc->m_OutputArguments.OutputStream[0].pTexture2D,
122 D3D12_RESOURCE_STATE_COMMON,
123 D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE));
124
125 for(D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS1 curInput : pD3D12Proc->m_ProcessInputs)
126 barrier_transitions.push_back(CD3DX12_RESOURCE_BARRIER::Transition(
127 curInput.InputStream[0].pTexture2D,
128 D3D12_RESOURCE_STATE_COMMON,
129 D3D12_RESOURCE_STATE_VIDEO_PROCESS_READ));
130
131 pD3D12Proc->m_spCommandList->ResourceBarrier(static_cast<uint32_t>(barrier_transitions.size()), barrier_transitions.data());
132
133 // Schedule process operation
134
135 pD3D12Proc->m_spCommandList->ProcessFrames1(pD3D12Proc->m_spVideoProcessor.Get(), &pD3D12Proc->m_OutputArguments, pD3D12Proc->m_ProcessInputs.size(), pD3D12Proc->m_ProcessInputs.data());
136
137 // Schedule reverse (back to common) transitions before command list closes for current frame
138
139 for (auto &BarrierDesc : barrier_transitions)
140 std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter);
141
142 pD3D12Proc->m_spCommandList->ResourceBarrier(static_cast<uint32_t>(barrier_transitions.size()), barrier_transitions.data());
143 }
144
145 void
d3d12_video_processor_process_frame(struct pipe_video_codec * codec,struct pipe_video_buffer * input_texture,const struct pipe_vpp_desc * process_properties)146 d3d12_video_processor_process_frame(struct pipe_video_codec *codec,
147 struct pipe_video_buffer *input_texture,
148 const struct pipe_vpp_desc *process_properties)
149 {
150 struct d3d12_video_processor * pD3D12Proc = (struct d3d12_video_processor *) codec;
151
152 // Get the underlying resources from the pipe_video_buffers
153 struct d3d12_video_buffer *pInputVideoBuffer = (struct d3d12_video_buffer *) input_texture;
154
155 // Make the resources permanently resident for video use
156 d3d12_promote_to_permanent_residency(pD3D12Proc->m_pD3D12Screen, pInputVideoBuffer->texture);
157
158 ID3D12Resource *pSrcD3D12Res = d3d12_resource_resource(pInputVideoBuffer->texture);
159
160 // y0 = top
161 // x0 = left
162 // x1 = right
163 // y1 = bottom
164
165 debug_printf("d3d12_video_processor_process_frame: Adding Input ID3D12Resource: %p to scene (Output target %p)\n", pSrcD3D12Res, pD3D12Proc->m_OutputArguments.OutputStream[0].pTexture2D);
166 debug_printf("d3d12_video_processor_process_frame: Input box: top: %d left: %d right: %d bottom: %d\n", process_properties->src_region.y0, process_properties->src_region.x0, process_properties->src_region.x1, process_properties->src_region.y1);
167 debug_printf("d3d12_video_processor_process_frame: Output box: top: %d left: %d right: %d bottom: %d\n", process_properties->dst_region.y0, process_properties->dst_region.x0, process_properties->dst_region.x1, process_properties->dst_region.y1);
168 debug_printf("d3d12_video_processor_process_frame: Requested alpha blend mode %d global alpha: %f \n", process_properties->blend.mode, process_properties->blend.global_alpha);
169
170 // Setup process frame arguments for current input texture.
171
172 unsigned curInputStreamIndex = pD3D12Proc->m_ProcessInputs.size();
173 D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS1 InputArguments = {
174 {
175 { // D3D12_VIDEO_PROCESS_INPUT_STREAM InputStream[0];
176 pSrcD3D12Res, // ID3D12Resource *pTexture2D;
177 0, // UINT Subresource
178 {//D3D12_VIDEO_PROCESS_REFERENCE_SET ReferenceSet;
179 0, //UINT NumPastFrames;
180 NULL, //ID3D12Resource **ppPastFrames;
181 NULL, // UINT *pPastSubresources;
182 0, //UINT NumFutureFrames;
183 NULL, //ID3D12Resource **ppFutureFrames;
184 NULL //UINT *pFutureSubresources;
185 }
186 },
187 { // D3D12_VIDEO_PROCESS_INPUT_STREAM InputStream[1];
188 NULL, //ID3D12Resource *pTexture2D;
189 0, //UINT Subresource;
190 {//D3D12_VIDEO_PROCESS_REFERENCE_SET ReferenceSet;
191 0, //UINT NumPastFrames;
192 NULL, //ID3D12Resource **ppPastFrames;
193 NULL, // UINT *pPastSubresources;
194 0, //UINT NumFutureFrames;
195 NULL, //ID3D12Resource **ppFutureFrames;
196 NULL //UINT *pFutureSubresources;
197 }
198 }
199 },
200 { // D3D12_VIDEO_PROCESS_TRANSFORM Transform;
201 // y0 = top
202 // x0 = left
203 // x1 = right
204 // y1 = bottom
205 // typedef struct _RECT
206 // {
207 // int left;
208 // int top;
209 // int right;
210 // int bottom;
211 // } RECT;
212 { process_properties->src_region.x0/*left*/, process_properties->src_region.y0/*top*/, process_properties->src_region.x1/*right*/, process_properties->src_region.y1/*bottom*/ },
213 { process_properties->dst_region.x0/*left*/, process_properties->dst_region.y0/*top*/, process_properties->dst_region.x1/*right*/, process_properties->dst_region.y1/*bottom*/ }, // D3D12_RECT DestinationRectangle;
214 pD3D12Proc->m_inputStreamDescs[curInputStreamIndex].EnableOrientation ? d3d12_video_processor_convert_pipe_rotation(process_properties->orientation) : D3D12_VIDEO_PROCESS_ORIENTATION_DEFAULT, // D3D12_VIDEO_PROCESS_ORIENTATION Orientation;
215 },
216 D3D12_VIDEO_PROCESS_INPUT_STREAM_FLAG_NONE,
217 { // D3D12_VIDEO_PROCESS_INPUT_STREAM_RATE RateInfo;
218 0,
219 0,
220 },
221 // INT FilterLevels[32];
222 {
223 0, // Trailing zeroes on the rest
224 },
225 //D3D12_VIDEO_PROCESS_ALPHA_BLENDING;
226 {
227 (process_properties->blend.mode == PIPE_VIDEO_VPP_BLEND_MODE_GLOBAL_ALPHA),
228 process_properties->blend.global_alpha
229 },
230 // D3D12_VIDEO_FIELD_TYPE FieldType
231 D3D12_VIDEO_FIELD_TYPE_NONE,
232 };
233
234 debug_printf("ProcessFrame InArgs Orientation %d \n\tSrc top: %d left: %d right: %d bottom: %d\n\tDst top: %d left: %d right: %d bottom: %d\n", InputArguments.Transform.Orientation,
235 InputArguments.Transform.SourceRectangle.top, InputArguments.Transform.SourceRectangle.left, InputArguments.Transform.SourceRectangle.right, InputArguments.Transform.SourceRectangle.bottom,
236 InputArguments.Transform.DestinationRectangle.top, InputArguments.Transform.DestinationRectangle.left, InputArguments.Transform.DestinationRectangle.right, InputArguments.Transform.DestinationRectangle.bottom);
237
238 pD3D12Proc->m_ProcessInputs.push_back(InputArguments);
239
240 ///
241 /// Flush work to the GPU and blocking wait until GPU finishes
242 ///
243 pD3D12Proc->m_needsGPUFlush = true;
244 }
245
246 void
d3d12_video_processor_destroy(struct pipe_video_codec * codec)247 d3d12_video_processor_destroy(struct pipe_video_codec * codec)
248 {
249 if (codec == nullptr) {
250 return;
251 }
252 d3d12_video_processor_flush(codec); // Flush pending work before destroying.
253
254 // Call dtor to make ComPtr work
255 struct d3d12_video_processor * pD3D12Proc = (struct d3d12_video_processor *) codec;
256 delete pD3D12Proc;
257 }
258
259 void
d3d12_video_processor_flush(struct pipe_video_codec * codec)260 d3d12_video_processor_flush(struct pipe_video_codec * codec)
261 {
262 struct d3d12_video_processor * pD3D12Proc = (struct d3d12_video_processor *) codec;
263 assert(pD3D12Proc);
264 assert(pD3D12Proc->m_spD3D12VideoDevice);
265 assert(pD3D12Proc->m_spCommandQueue);
266
267 // Flush buffer_subdata batch and Wait the m_spCommandQueue for GPU upload completion
268 // before executing the current batch below. Input objects coming from the pipe_context (ie. input texture) must be fully finished working with before processor can read them.
269 struct pipe_fence_handle *completion_fence = NULL;
270 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush - Flushing pD3D12Proc->m_pD3D12Context->base. and GPU sync between Video/Context queues before flushing Video Process Queue.\n");
271 pD3D12Proc->m_pD3D12Context->base.flush(&pD3D12Proc->m_pD3D12Context->base, &completion_fence, PIPE_FLUSH_ASYNC | PIPE_FLUSH_HINT_FINISH);
272 assert(completion_fence);
273 struct d3d12_fence *casted_completion_fence = d3d12_fence(completion_fence);
274 pD3D12Proc->m_spCommandQueue->Wait(casted_completion_fence->cmdqueue_fence, casted_completion_fence->value);
275
276 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush started. Will flush video queue work and CPU wait on "
277 "fenceValue: %d\n",
278 pD3D12Proc->m_fenceValue);
279
280 if (!pD3D12Proc->m_needsGPUFlush) {
281 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush started. Nothing to flush, all up to date.\n");
282 } else {
283 HRESULT hr = pD3D12Proc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
284 if (hr != S_OK) {
285 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush"
286 " - D3D12Device was removed BEFORE commandlist "
287 "execution with HR %x.\n",
288 hr);
289 goto flush_fail;
290 }
291
292 // Close and execute command list and wait for idle on CPU blocking
293 // this method before resetting list and allocator for next submission.
294
295 if (pD3D12Proc->m_transitionsBeforeCloseCmdList.size() > 0) {
296 pD3D12Proc->m_spCommandList->ResourceBarrier(pD3D12Proc->m_transitionsBeforeCloseCmdList.size(),
297 pD3D12Proc->m_transitionsBeforeCloseCmdList.data());
298 pD3D12Proc->m_transitionsBeforeCloseCmdList.clear();
299 }
300
301 hr = pD3D12Proc->m_spCommandList->Close();
302 if (FAILED(hr)) {
303 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush - Can't close command list with HR %x\n", hr);
304 goto flush_fail;
305 }
306
307 ID3D12CommandList *ppCommandLists[1] = { pD3D12Proc->m_spCommandList.Get() };
308 pD3D12Proc->m_spCommandQueue->ExecuteCommandLists(1, ppCommandLists);
309 pD3D12Proc->m_spCommandQueue->Signal(pD3D12Proc->m_spFence.Get(), pD3D12Proc->m_fenceValue);
310 pD3D12Proc->m_spFence->SetEventOnCompletion(pD3D12Proc->m_fenceValue, nullptr);
311 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush - ExecuteCommandLists finished on signal with "
312 "fenceValue: %d\n",
313 pD3D12Proc->m_fenceValue);
314
315 hr = pD3D12Proc->m_spCommandAllocator->Reset();
316 if (FAILED(hr)) {
317 debug_printf(
318 "[d3d12_video_processor] d3d12_video_processor_flush - resetting ID3D12CommandAllocator failed with HR %x\n",
319 hr);
320 goto flush_fail;
321 }
322
323 hr = pD3D12Proc->m_spCommandList->Reset(pD3D12Proc->m_spCommandAllocator.Get());
324 if (FAILED(hr)) {
325 debug_printf(
326 "[d3d12_video_processor] d3d12_video_processor_flush - resetting ID3D12GraphicsCommandList failed with HR %x\n",
327 hr);
328 goto flush_fail;
329 }
330
331 // Validate device was not removed
332 hr = pD3D12Proc->m_pD3D12Screen->dev->GetDeviceRemovedReason();
333 if (hr != S_OK) {
334 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush"
335 " - D3D12Device was removed AFTER commandlist "
336 "execution with HR %x, but wasn't before.\n",
337 hr);
338 goto flush_fail;
339 }
340
341 debug_printf(
342 "[d3d12_video_processor] d3d12_video_processor_flush - GPU signaled execution finalized for fenceValue: %d\n",
343 pD3D12Proc->m_fenceValue);
344
345 pD3D12Proc->m_fenceValue++;
346 pD3D12Proc->m_needsGPUFlush = false;
347 }
348 pD3D12Proc->m_ProcessInputs.clear();
349 // Free the fence after completion finished
350 if(completion_fence)
351 pD3D12Proc->m_pD3D12Screen->base.fence_reference(&pD3D12Proc->m_pD3D12Screen->base, &completion_fence, NULL);
352
353 return;
354
355 flush_fail:
356 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush failed for fenceValue: %d\n", pD3D12Proc->m_fenceValue);
357 assert(false);
358 }
359
360 struct pipe_video_codec *
d3d12_video_processor_create(struct pipe_context * context,const struct pipe_video_codec * codec)361 d3d12_video_processor_create(struct pipe_context *context, const struct pipe_video_codec *codec)
362 {
363 ///
364 /// Initialize d3d12_video_processor
365 ///
366
367 // Not using new doesn't call ctor and the initializations in the class declaration are lost
368 struct d3d12_video_processor *pD3D12Proc = new d3d12_video_processor;
369
370 pD3D12Proc->base = *codec;
371
372 pD3D12Proc->base.context = context;
373 pD3D12Proc->base.width = codec->width;
374 pD3D12Proc->base.height = codec->height;
375 pD3D12Proc->base.destroy = d3d12_video_processor_destroy;
376 pD3D12Proc->base.begin_frame = d3d12_video_processor_begin_frame;
377 pD3D12Proc->base.process_frame = d3d12_video_processor_process_frame;
378 pD3D12Proc->base.end_frame = d3d12_video_processor_end_frame;
379 pD3D12Proc->base.flush = d3d12_video_processor_flush;
380
381 ///
382
383 ///
384 /// Try initializing D3D12 Video device and check for device caps
385 ///
386
387 struct d3d12_context *pD3D12Ctx = (struct d3d12_context *) context;
388 pD3D12Proc->m_pD3D12Context = pD3D12Ctx;
389 pD3D12Proc->m_pD3D12Screen = d3d12_screen(pD3D12Ctx->base.screen);
390
391 // Assume defaults for now, can re-create if necessary when d3d12_video_processor_end_frame kicks off the processing
392 DXGI_COLOR_SPACE_TYPE InputColorSpace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
393 std::vector<DXGI_FORMAT> InputFormats = { DXGI_FORMAT_NV12 };
394 DXGI_FORMAT OutputFormat = DXGI_FORMAT_NV12;
395 DXGI_COLOR_SPACE_TYPE OutputColorSpace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
396
397 ///
398 /// Create processor objects
399 ///
400 if (FAILED(pD3D12Proc->m_pD3D12Screen->dev->QueryInterface(
401 IID_PPV_ARGS(pD3D12Proc->m_spD3D12VideoDevice.GetAddressOf())))) {
402 debug_printf("[d3d12_video_processor] d3d12_video_create_processor - D3D12 Device has no Video support\n");
403 goto failed;
404 }
405
406 if (!d3d12_video_processor_check_caps_and_create_processor(pD3D12Proc, InputFormats, InputColorSpace, OutputFormat, OutputColorSpace)) {
407 debug_printf("[d3d12_video_processor] d3d12_video_create_processor - Failure on "
408 "d3d12_video_processor_check_caps_and_create_processor\n");
409 goto failed;
410 }
411
412 if (!d3d12_video_processor_create_command_objects(pD3D12Proc)) {
413 debug_printf(
414 "[d3d12_video_processor] d3d12_video_create_processor - Failure on d3d12_video_processor_create_command_objects\n");
415 goto failed;
416 }
417
418 debug_printf("[d3d12_video_processor] d3d12_video_create_processor - Created successfully!\n");
419
420 return &pD3D12Proc->base;
421
422 failed:
423 if (pD3D12Proc != nullptr) {
424 d3d12_video_processor_destroy(&pD3D12Proc->base);
425 }
426
427 return nullptr;
428 }
429
430 bool
d3d12_video_processor_check_caps_and_create_processor(struct d3d12_video_processor * pD3D12Proc,std::vector<DXGI_FORMAT> InputFormats,DXGI_COLOR_SPACE_TYPE InputColorSpace,DXGI_FORMAT OutputFormat,DXGI_COLOR_SPACE_TYPE OutputColorSpace)431 d3d12_video_processor_check_caps_and_create_processor(struct d3d12_video_processor *pD3D12Proc,
432 std::vector<DXGI_FORMAT> InputFormats,
433 DXGI_COLOR_SPACE_TYPE InputColorSpace,
434 DXGI_FORMAT OutputFormat,
435 DXGI_COLOR_SPACE_TYPE OutputColorSpace)
436 {
437 HRESULT hr = S_OK;
438
439 D3D12_VIDEO_FIELD_TYPE FieldType = D3D12_VIDEO_FIELD_TYPE_NONE;
440 D3D12_VIDEO_FRAME_STEREO_FORMAT StereoFormat = D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE;
441 DXGI_RATIONAL FrameRate = { 30, 1 };
442 DXGI_RATIONAL AspectRatio = { 1, 1 };
443
444 struct ResolStruct {
445 uint Width;
446 uint Height;
447 };
448
449 ResolStruct resolutionsList[] = {
450 { 8192, 8192 }, // 8k
451 { 8192, 4320 }, // 8k - alternative
452 { 7680, 4800 }, // 8k - alternative
453 { 7680, 4320 }, // 8k - alternative
454 { 4096, 2304 }, // 2160p (4K)
455 { 4096, 2160 }, // 2160p (4K) - alternative
456 { 2560, 1440 }, // 1440p
457 { 1920, 1200 }, // 1200p
458 { 1920, 1080 }, // 1080p
459 { 1280, 720 }, // 720p
460 { 800, 600 },
461 };
462
463 pD3D12Proc->m_SupportCaps =
464 {
465 0, // NodeIndex
466 { resolutionsList[0].Width, resolutionsList[0].Height, { InputFormats[0], InputColorSpace } },
467 FieldType,
468 StereoFormat,
469 FrameRate,
470 { OutputFormat, OutputColorSpace },
471 StereoFormat,
472 FrameRate,
473 };
474
475 uint32_t idxResol = 0;
476 bool bSupportsAny = false;
477 while ((idxResol < ARRAY_SIZE(resolutionsList)) && !bSupportsAny) {
478 pD3D12Proc->m_SupportCaps.InputSample.Width = resolutionsList[idxResol].Width;
479 pD3D12Proc->m_SupportCaps.InputSample.Height = resolutionsList[idxResol].Height;
480 if (SUCCEEDED(pD3D12Proc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_SUPPORT, &pD3D12Proc->m_SupportCaps, sizeof(pD3D12Proc->m_SupportCaps)))) {
481 bSupportsAny = ((pD3D12Proc->m_SupportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != 0);
482 }
483 idxResol++;
484 }
485
486 if ((pD3D12Proc->m_SupportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED)
487 {
488 if((pD3D12Proc->m_SupportCaps.SupportFlags & D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) != D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED) {
489 debug_printf("[d3d12_video_processor] d3d12_video_processor_check_caps_and_create_processor - D3D12_VIDEO_PROCESS_SUPPORT_FLAG_SUPPORTED not returned by driver. "
490 "failed with SupportFlags %x\n",
491 pD3D12Proc->m_SupportCaps.SupportFlags);
492 }
493 }
494
495 D3D12_VIDEO_PROCESS_FILTER_FLAGS enabledFilterFlags = D3D12_VIDEO_PROCESS_FILTER_FLAG_NONE;
496
497 bool enableOrientation = (
498 ((pD3D12Proc->m_SupportCaps.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_ROTATION) != 0)
499 || ((pD3D12Proc->m_SupportCaps.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_FLIP) != 0)
500 );
501
502 D3D12_VIDEO_PROCESS_INPUT_STREAM_DESC inputStreamDesc = {
503 InputFormats[0],
504 InputColorSpace,
505 AspectRatio, // SourceAspectRatio;
506 AspectRatio, // DestinationAspectRatio;
507 FrameRate, // FrameRate
508 pD3D12Proc->m_SupportCaps.ScaleSupport.OutputSizeRange, // SourceSizeRange
509 pD3D12Proc->m_SupportCaps.ScaleSupport.OutputSizeRange, // DestinationSizeRange
510 enableOrientation,
511 enabledFilterFlags,
512 StereoFormat,
513 FieldType,
514 D3D12_VIDEO_PROCESS_DEINTERLACE_FLAG_NONE,
515 ((pD3D12Proc->m_SupportCaps.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_ALPHA_BLENDING) != 0)
516 && ((pD3D12Proc->m_SupportCaps.FeatureSupport & D3D12_VIDEO_PROCESS_FEATURE_FLAG_ALPHA_FILL) != 0), // EnableAlphaBlending
517 {}, // LumaKey
518 0, // NumPastFrames
519 0, // NumFutureFrames
520 FALSE // EnableAutoProcessing
521 };
522
523 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_DESC outputStreamDesc =
524 {
525 pD3D12Proc->m_SupportCaps.OutputFormat.Format,
526 OutputColorSpace,
527 D3D12_VIDEO_PROCESS_ALPHA_FILL_MODE_OPAQUE, // AlphaFillMode
528 0u, // AlphaFillModeSourceStreamIndex
529 {0, 0, 0, 0}, // BackgroundColor
530 FrameRate, // FrameRate
531 FALSE // EnableStereo
532 };
533
534 // gets the required past/future frames for VP creation
535 {
536 D3D12_FEATURE_DATA_VIDEO_PROCESS_REFERENCE_INFO referenceInfo = {};
537 referenceInfo.NodeIndex = 0;
538 D3D12_VIDEO_PROCESS_FEATURE_FLAGS featureFlags = D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
539 featureFlags |= outputStreamDesc.AlphaFillMode ? D3D12_VIDEO_PROCESS_FEATURE_FLAG_ALPHA_FILL : D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
540 featureFlags |= inputStreamDesc.LumaKey.Enable ? D3D12_VIDEO_PROCESS_FEATURE_FLAG_LUMA_KEY : D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
541 featureFlags |= (inputStreamDesc.StereoFormat != D3D12_VIDEO_FRAME_STEREO_FORMAT_NONE || outputStreamDesc.EnableStereo) ? D3D12_VIDEO_PROCESS_FEATURE_FLAG_STEREO : D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
542 featureFlags |= inputStreamDesc.EnableOrientation ? D3D12_VIDEO_PROCESS_FEATURE_FLAG_ROTATION | D3D12_VIDEO_PROCESS_FEATURE_FLAG_FLIP : D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
543 featureFlags |= inputStreamDesc.EnableAlphaBlending ? D3D12_VIDEO_PROCESS_FEATURE_FLAG_ALPHA_BLENDING : D3D12_VIDEO_PROCESS_FEATURE_FLAG_NONE;
544
545 referenceInfo.DeinterlaceMode = inputStreamDesc.DeinterlaceMode;
546 referenceInfo.Filters = inputStreamDesc.FilterFlags;
547 referenceInfo.FeatureSupport = featureFlags;
548 referenceInfo.InputFrameRate = inputStreamDesc.FrameRate;
549 referenceInfo.OutputFrameRate = outputStreamDesc.FrameRate;
550 referenceInfo.EnableAutoProcessing = inputStreamDesc.EnableAutoProcessing;
551
552 hr = pD3D12Proc->m_spD3D12VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_PROCESS_REFERENCE_INFO, &referenceInfo, sizeof(referenceInfo));
553 if (FAILED(hr)) {
554 debug_printf("[d3d12_video_processor] d3d12_video_processor_check_caps_and_create_processor - CheckFeatureSupport "
555 "failed with HR %x\n",
556 hr);
557 return false;
558 }
559
560 inputStreamDesc.NumPastFrames = referenceInfo.PastFrames;
561 inputStreamDesc.NumFutureFrames = referenceInfo.FutureFrames;
562 }
563
564 pD3D12Proc->m_outputStreamDesc = outputStreamDesc;
565
566 debug_printf("[d3d12_video_processor]\t Creating Video Processor\n");
567 debug_printf("[d3d12_video_processor]\t NumInputs: %d\n", (int) InputFormats.size());
568
569 pD3D12Proc->m_inputStreamDescs.clear();
570 for (unsigned i = 0; i < InputFormats.size(); i++)
571 {
572 inputStreamDesc.Format = InputFormats[i];
573 pD3D12Proc->m_inputStreamDescs.push_back(inputStreamDesc);
574 debug_printf("[d3d12_video_processor]\t Input Stream #%d Format: %d\n", i, inputStreamDesc.Format);
575 }
576 debug_printf("[d3d12_video_processor]\t Output Stream Format: %d\n", pD3D12Proc->m_outputStreamDesc.Format);
577
578 hr = pD3D12Proc->m_spD3D12VideoDevice->CreateVideoProcessor(pD3D12Proc->m_NodeMask,
579 &pD3D12Proc->m_outputStreamDesc,
580 pD3D12Proc->m_inputStreamDescs.size(),
581 pD3D12Proc->m_inputStreamDescs.data(),
582 IID_PPV_ARGS(pD3D12Proc->m_spVideoProcessor.GetAddressOf()));
583 if (FAILED(hr)) {
584 debug_printf("[d3d12_video_processor] d3d12_video_processor_check_caps_and_create_processor - CreateVideoProcessor "
585 "failed with HR %x\n",
586 hr);
587 return false;
588 }
589
590 return true;
591 }
592
593 bool
d3d12_video_processor_create_command_objects(struct d3d12_video_processor * pD3D12Proc)594 d3d12_video_processor_create_command_objects(struct d3d12_video_processor *pD3D12Proc)
595 {
596 assert(pD3D12Proc->m_spD3D12VideoDevice);
597
598 D3D12_COMMAND_QUEUE_DESC commandQueueDesc = { D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS };
599 HRESULT hr = pD3D12Proc->m_pD3D12Screen->dev->CreateCommandQueue(
600 &commandQueueDesc,
601 IID_PPV_ARGS(pD3D12Proc->m_spCommandQueue.GetAddressOf()));
602
603 if (FAILED(hr)) {
604 debug_printf("[d3d12_video_processor] d3d12_video_processor_create_command_objects - Call to CreateCommandQueue "
605 "failed with HR %x\n",
606 hr);
607 return false;
608 }
609
610 hr = pD3D12Proc->m_pD3D12Screen->dev->CreateFence(0,
611 D3D12_FENCE_FLAG_NONE,
612 IID_PPV_ARGS(&pD3D12Proc->m_spFence));
613
614 if (FAILED(hr)) {
615 debug_printf(
616 "[d3d12_video_processor] d3d12_video_processor_create_command_objects - Call to CreateFence failed with HR %x\n",
617 hr);
618 return false;
619 }
620
621 hr = pD3D12Proc->m_pD3D12Screen->dev->CreateCommandAllocator(
622 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
623 IID_PPV_ARGS(pD3D12Proc->m_spCommandAllocator.GetAddressOf()));
624
625 if (FAILED(hr)) {
626 debug_printf("[d3d12_video_processor] d3d12_video_processor_create_command_objects - Call to "
627 "CreateCommandAllocator failed with HR %x\n",
628 hr);
629 return false;
630 }
631
632 hr = pD3D12Proc->m_pD3D12Screen->dev->CreateCommandList(0,
633 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS,
634 pD3D12Proc->m_spCommandAllocator.Get(),
635 nullptr,
636 IID_PPV_ARGS(pD3D12Proc->m_spCommandList.GetAddressOf()));
637
638 if (FAILED(hr)) {
639 debug_printf("[d3d12_video_processor] d3d12_video_processor_create_command_objects - Call to CreateCommandList "
640 "failed with HR %x\n",
641 hr);
642 return false;
643 }
644
645 return true;
646 }
647
648 D3D12_VIDEO_PROCESS_ORIENTATION
d3d12_video_processor_convert_pipe_rotation(enum pipe_video_vpp_orientation orientation_flags)649 d3d12_video_processor_convert_pipe_rotation(enum pipe_video_vpp_orientation orientation_flags)
650 {
651 D3D12_VIDEO_PROCESS_ORIENTATION result = D3D12_VIDEO_PROCESS_ORIENTATION_DEFAULT;
652
653 if(orientation_flags & PIPE_VIDEO_VPP_ROTATION_90)
654 {
655 result = (orientation_flags & PIPE_VIDEO_VPP_FLIP_HORIZONTAL) ? D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_90_FLIP_HORIZONTAL : D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_90;
656 debug_printf("d3d12_video_processor_process_frame: Orientation Mode: %s\n", (orientation_flags & PIPE_VIDEO_VPP_FLIP_HORIZONTAL) ? "D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_90_FLIP_HORIZONTAL" : "D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_90");
657 }
658 else if(orientation_flags & PIPE_VIDEO_VPP_ROTATION_180)
659 {
660 result = D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_180;
661 debug_printf("d3d12_video_processor_process_frame: Orientation Mode: D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_180\n");
662 }
663 else if(orientation_flags & PIPE_VIDEO_VPP_ROTATION_270)
664 {
665 result = (orientation_flags & PIPE_VIDEO_VPP_FLIP_HORIZONTAL) ? D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_270_FLIP_HORIZONTAL : D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_270;
666 debug_printf("d3d12_video_processor_process_frame: Orientation Mode: %s\n", (orientation_flags & PIPE_VIDEO_VPP_FLIP_HORIZONTAL) ? "D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_270_FLIP_HORIZONTAL" : "D3D12_VIDEO_PROCESS_ORIENTATION_CLOCKWISE_270");
667 }
668 else if(orientation_flags & PIPE_VIDEO_VPP_FLIP_HORIZONTAL)
669 {
670 result = D3D12_VIDEO_PROCESS_ORIENTATION_FLIP_HORIZONTAL;
671 debug_printf("d3d12_video_processor_process_frame: Orientation Mode: D3D12_VIDEO_PROCESS_ORIENTATION_FLIP_HORIZONTAL\n");
672 }
673 else if(orientation_flags & PIPE_VIDEO_VPP_FLIP_VERTICAL)
674 {
675 result = D3D12_VIDEO_PROCESS_ORIENTATION_FLIP_VERTICAL;
676 debug_printf("d3d12_video_processor_process_frame: Orientation Mode: D3D12_VIDEO_PROCESS_ORIENTATION_FLIP_VERTICAL\n");
677 }
678
679 return result;
680 }
681