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_video_buffer.h"
25#include "d3d12_resource.h"
26#include "d3d12_video_dec.h"
27#include "d3d12_residency.h"
28
29#include "util/format/u_format.h"
30#include "util/u_inlines.h"
31#include "util/u_memory.h"
32#include "util/u_video.h"
33#include "vl/vl_video_buffer.h"
34#include "util/u_sampler.h"
35
36/**
37 * creates a video buffer
38 */
39struct pipe_video_buffer *
40d3d12_video_buffer_create(struct pipe_context *pipe, const struct pipe_video_buffer *tmpl)
41{
42   assert(pipe);
43   assert(tmpl);
44
45   ///
46   /// Initialize d3d12_video_buffer
47   ///
48
49
50   if (!(tmpl->buffer_format == PIPE_FORMAT_NV12)) {
51      debug_printf("[d3d12_video_buffer] buffer_format is only supported as PIPE_FORMAT_NV12.\n");
52      return nullptr;
53   }
54
55   if (!(pipe_format_to_chroma_format(tmpl->buffer_format) == PIPE_VIDEO_CHROMA_FORMAT_420)) {
56      debug_printf(
57         "[d3d12_video_buffer] tmpl->buffer_format only supported as a PIPE_VIDEO_CHROMA_FORMAT_420 format.\n");
58      return nullptr;
59   }
60
61   // Not using new doesn't call ctor and the initializations in the class declaration are lost
62   struct d3d12_video_buffer *pD3D12VideoBuffer = new d3d12_video_buffer;
63
64   // Fill base template
65   pD3D12VideoBuffer->base               = *tmpl;
66   pD3D12VideoBuffer->base.buffer_format = tmpl->buffer_format;
67   pD3D12VideoBuffer->base.context       = pipe;
68   pD3D12VideoBuffer->base.width         = tmpl->width;
69   pD3D12VideoBuffer->base.height        = tmpl->height;
70   pD3D12VideoBuffer->base.interlaced    = tmpl->interlaced;
71   pD3D12VideoBuffer->base.associated_data = nullptr;
72   pD3D12VideoBuffer->base.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET | PIPE_BIND_DISPLAY_TARGET;
73
74   // Fill vtable
75   pD3D12VideoBuffer->base.destroy                     = d3d12_video_buffer_destroy;
76   pD3D12VideoBuffer->base.get_sampler_view_planes     = d3d12_video_buffer_get_sampler_view_planes;
77   pD3D12VideoBuffer->base.get_sampler_view_components = d3d12_video_buffer_get_sampler_view_components;
78   pD3D12VideoBuffer->base.get_surfaces                = d3d12_video_buffer_get_surfaces;
79   pD3D12VideoBuffer->base.destroy_associated_data     = d3d12_video_buffer_destroy_associated_data;
80
81   struct pipe_resource templ;
82   memset(&templ, 0, sizeof(templ));
83   templ.target     = PIPE_TEXTURE_2D;
84   templ.bind       = pD3D12VideoBuffer->base.bind;
85   templ.format     = pD3D12VideoBuffer->base.buffer_format;
86   // YUV 4:2:0 formats in D3D12 need to have multiple of 2 dimensions
87   templ.width0     = align(pD3D12VideoBuffer->base.width, 2);
88   templ.height0    = align(pD3D12VideoBuffer->base.height, 2);
89   templ.depth0     = 1;
90   templ.array_size = 1;
91   templ.flags      = 0;
92
93   // This calls d3d12_create_resource as the function ptr is set in d3d12_screen.resource_create
94   pD3D12VideoBuffer->texture = (struct d3d12_resource *) pipe->screen->resource_create(pipe->screen, &templ);
95   d3d12_promote_to_permanent_residency((struct d3d12_screen*) pipe->screen, pD3D12VideoBuffer->texture);
96
97   if (pD3D12VideoBuffer->texture == nullptr) {
98      debug_printf("[d3d12_video_buffer] d3d12_video_buffer_create - Call to resource_create() to create "
99                      "d3d12_resource failed\n");
100      goto failed;
101   }
102
103   pD3D12VideoBuffer->num_planes = util_format_get_num_planes(pD3D12VideoBuffer->texture->overall_format);
104   assert(pD3D12VideoBuffer->num_planes == 2);
105   return &pD3D12VideoBuffer->base;
106
107failed:
108   d3d12_video_buffer_destroy((struct pipe_video_buffer *) pD3D12VideoBuffer);
109
110   return nullptr;
111}
112
113/**
114 * destroy this video buffer
115 */
116void
117d3d12_video_buffer_destroy(struct pipe_video_buffer *buffer)
118{
119   struct d3d12_video_buffer *pD3D12VideoBuffer = (struct d3d12_video_buffer *) buffer;
120
121   // Destroy pD3D12VideoBuffer->texture (if any)
122   if (pD3D12VideoBuffer->texture) {
123      pipe_resource *pBaseResource = &pD3D12VideoBuffer->texture->base.b;
124      pipe_resource_reference(&pBaseResource, NULL);
125   }
126
127   // Destroy associated data (if any)
128   if (pD3D12VideoBuffer->base.associated_data != nullptr) {
129      d3d12_video_buffer_destroy_associated_data(pD3D12VideoBuffer->base.associated_data);
130      // Set to nullptr after cleanup, no dangling pointers
131      pD3D12VideoBuffer->base.associated_data = nullptr;
132   }
133
134   // Destroy (if any) codec where the associated data came from
135   if (pD3D12VideoBuffer->base.codec != nullptr) {
136      d3d12_video_decoder_destroy(pD3D12VideoBuffer->base.codec);
137      // Set to nullptr after cleanup, no dangling pointers
138      pD3D12VideoBuffer->base.codec = nullptr;
139   }
140
141   for (uint i = 0; i < pD3D12VideoBuffer->surfaces.size(); ++i) {
142      if (pD3D12VideoBuffer->surfaces[i] != NULL) {
143         pipe_surface_reference(&pD3D12VideoBuffer->surfaces[i], NULL);
144      }
145   }
146
147   for (uint i = 0; i < pD3D12VideoBuffer->sampler_view_planes.size(); ++i) {
148      if (pD3D12VideoBuffer->sampler_view_planes[i] != NULL) {
149         pipe_sampler_view_reference(&pD3D12VideoBuffer->sampler_view_planes[i], NULL);
150      }
151   }
152
153   for (uint i = 0; i < pD3D12VideoBuffer->sampler_view_components.size(); ++i) {
154      if (pD3D12VideoBuffer->sampler_view_components[i] != NULL) {
155         pipe_sampler_view_reference(&pD3D12VideoBuffer->sampler_view_components[i], NULL);
156      }
157   }
158
159   delete pD3D12VideoBuffer;
160}
161
162/*
163 * destroy the associated data
164 */
165void
166d3d12_video_buffer_destroy_associated_data(void *associated_data)
167{ }
168
169/**
170 * get an individual surfaces for each plane
171 */
172struct pipe_surface **
173d3d12_video_buffer_get_surfaces(struct pipe_video_buffer *buffer)
174{
175   assert(buffer);
176   struct d3d12_video_buffer *pD3D12VideoBuffer = (struct d3d12_video_buffer *) buffer;
177   struct pipe_context *      pipe              = pD3D12VideoBuffer->base.context;
178   struct pipe_surface        surface_template  = {};
179
180   // Some video frameworks iterate over [0..VL_MAX_SURFACES) and ignore the nullptr entries
181   // So we have to null initialize the other surfaces not used from [num_planes..VL_MAX_SURFACES)
182   // Like in src/gallium/frontends/va/surface.c
183   pD3D12VideoBuffer->surfaces.resize(VL_MAX_SURFACES, nullptr);
184
185   // pCurPlaneResource refers to the planar resource, not the overall resource.
186   // in d3d12_resource this is handled by having a linked list of planes with
187   // d3dRes->base.next ptr to next plane resource
188   // starting with the plane 0 being the overall resource
189   struct pipe_resource *pCurPlaneResource = &pD3D12VideoBuffer->texture->base.b;
190
191   for (uint PlaneSlice = 0; PlaneSlice < pD3D12VideoBuffer->num_planes; ++PlaneSlice) {
192      if (!pD3D12VideoBuffer->surfaces[PlaneSlice]) {
193         memset(&surface_template, 0, sizeof(surface_template));
194         surface_template.format =
195            util_format_get_plane_format(pD3D12VideoBuffer->texture->overall_format, PlaneSlice);
196
197         pD3D12VideoBuffer->surfaces[PlaneSlice] =
198            pipe->create_surface(pipe, pCurPlaneResource, &surface_template);
199
200         if (!pD3D12VideoBuffer->surfaces[PlaneSlice]) {
201            goto error;
202         }
203      }
204      pCurPlaneResource = pCurPlaneResource->next;
205   }
206
207   return pD3D12VideoBuffer->surfaces.data();
208
209error:
210   for (uint PlaneSlice = 0; PlaneSlice < pD3D12VideoBuffer->num_planes; ++PlaneSlice) {
211      pipe_surface_reference(&pD3D12VideoBuffer->surfaces[PlaneSlice], NULL);
212   }
213
214   return nullptr;
215}
216
217/**
218 * get an individual sampler view for each plane
219 */
220struct pipe_sampler_view **
221d3d12_video_buffer_get_sampler_view_planes(struct pipe_video_buffer *buffer)
222{
223   assert(buffer);
224   struct d3d12_video_buffer *pD3D12VideoBuffer = (struct d3d12_video_buffer *) buffer;
225   struct pipe_context *      pipe              = pD3D12VideoBuffer->base.context;
226   struct pipe_sampler_view   samplerViewTemplate;
227
228   // Some video frameworks iterate over [0..VL_MAX_SURFACES) and ignore the nullptr entries
229   // So we have to null initialize the other surfaces not used from [num_planes..VL_MAX_SURFACES)
230   // Like in src/gallium/frontends/vdpau/surface.c
231   pD3D12VideoBuffer->sampler_view_planes.resize(VL_MAX_SURFACES, nullptr);
232
233   // pCurPlaneResource refers to the planar resource, not the overall resource.
234   // in d3d12_resource this is handled by having a linked list of planes with
235   // d3dRes->base.next ptr to next plane resource
236   // starting with the plane 0 being the overall resource
237   struct pipe_resource *pCurPlaneResource = &pD3D12VideoBuffer->texture->base.b;
238
239   for (uint i = 0; i < pD3D12VideoBuffer->num_planes; ++i) {
240      if (!pD3D12VideoBuffer->sampler_view_planes[i]) {
241         assert(pCurPlaneResource);   // the d3d12_resource has a linked list with the exact name of number of elements
242                                      // as planes
243
244         memset(&samplerViewTemplate, 0, sizeof(samplerViewTemplate));
245         u_sampler_view_default_template(&samplerViewTemplate, pCurPlaneResource, pCurPlaneResource->format);
246
247         pD3D12VideoBuffer->sampler_view_planes[i] =
248            pipe->create_sampler_view(pipe, pCurPlaneResource, &samplerViewTemplate);
249
250         if (!pD3D12VideoBuffer->sampler_view_planes[i]) {
251            goto error;
252         }
253      }
254
255      pCurPlaneResource = pCurPlaneResource->next;
256   }
257
258   return pD3D12VideoBuffer->sampler_view_planes.data();
259
260error:
261   for (uint i = 0; i < pD3D12VideoBuffer->num_planes; ++i) {
262      pipe_sampler_view_reference(&pD3D12VideoBuffer->sampler_view_planes[i], NULL);
263   }
264
265   return nullptr;
266}
267
268/**
269 * get an individual sampler view for each component
270 */
271struct pipe_sampler_view **
272d3d12_video_buffer_get_sampler_view_components(struct pipe_video_buffer *buffer)
273{
274   assert(buffer);
275   struct d3d12_video_buffer *pD3D12VideoBuffer = (struct d3d12_video_buffer *) buffer;
276   struct pipe_context *      pipe              = pD3D12VideoBuffer->base.context;
277   struct pipe_sampler_view   samplerViewTemplate;
278
279   // pCurPlaneResource refers to the planar resource, not the overall resource.
280   // in d3d12_resource this is handled by having a linked list of planes with
281   // d3dRes->base.next ptr to next plane resource
282   // starting with the plane 0 being the overall resource
283   struct pipe_resource *pCurPlaneResource = &pD3D12VideoBuffer->texture->base.b;
284
285   // At the end of the loop, "component" will have the total number of items valid in sampler_view_components
286   // since component can end up being <= VL_NUM_COMPONENTS, we assume VL_NUM_COMPONENTS first and then resize/adjust to
287   // fit the container size pD3D12VideoBuffer->sampler_view_components to the actual components number
288   pD3D12VideoBuffer->sampler_view_components.resize(VL_NUM_COMPONENTS, nullptr);
289   uint component = 0;
290
291   for (uint i = 0; i < pD3D12VideoBuffer->num_planes; ++i) {
292      // For example num_components would be 1 for the Y plane (R8 in NV12), 2 for the UV plane (R8G8 in NV12)
293      unsigned num_components = util_format_get_nr_components(pCurPlaneResource->format);
294
295      for (uint j = 0; j < num_components; ++j, ++component) {
296         assert(component < VL_NUM_COMPONENTS);
297
298         if (!pD3D12VideoBuffer->sampler_view_components[component]) {
299            memset(&samplerViewTemplate, 0, sizeof(samplerViewTemplate));
300            u_sampler_view_default_template(&samplerViewTemplate, pCurPlaneResource, pCurPlaneResource->format);
301            samplerViewTemplate.swizzle_r = samplerViewTemplate.swizzle_g = samplerViewTemplate.swizzle_b =
302               PIPE_SWIZZLE_X + j;
303            samplerViewTemplate.swizzle_a = PIPE_SWIZZLE_1;
304
305            pD3D12VideoBuffer->sampler_view_components[component] =
306               pipe->create_sampler_view(pipe, pCurPlaneResource, &samplerViewTemplate);
307            if (!pD3D12VideoBuffer->sampler_view_components[component]) {
308               goto error;
309            }
310         }
311      }
312
313      pCurPlaneResource = pCurPlaneResource->next;
314   }
315
316   // Adjust size to fit component <= VL_NUM_COMPONENTS
317   pD3D12VideoBuffer->sampler_view_components.resize(component);
318
319   return pD3D12VideoBuffer->sampler_view_components.data();
320
321error:
322   for (uint i = 0; i < pD3D12VideoBuffer->num_planes; ++i) {
323      pipe_sampler_view_reference(&pD3D12VideoBuffer->sampler_view_components[i], NULL);
324   }
325
326   return nullptr;
327}
328