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