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 32void 33d3d12_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 67void 68d3d12_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 145void 146d3d12_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 246void 247d3d12_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 259void 260d3d12_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 355flush_fail: 356 debug_printf("[d3d12_video_processor] d3d12_video_processor_flush failed for fenceValue: %d\n", pD3D12Proc->m_fenceValue); 357 assert(false); 358} 359 360struct pipe_video_codec * 361d3d12_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 422failed: 423 if (pD3D12Proc != nullptr) { 424 d3d12_video_processor_destroy(&pD3D12Proc->base); 425 } 426 427 return nullptr; 428} 429 430bool 431d3d12_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 593bool 594d3d12_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 648D3D12_VIDEO_PROCESS_ORIENTATION 649d3d12_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