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 */
39 struct pipe_video_buffer *
d3d12_video_buffer_create(struct pipe_context * pipe,const struct pipe_video_buffer * tmpl)40 d3d12_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
107 failed:
108 d3d12_video_buffer_destroy((struct pipe_video_buffer *) pD3D12VideoBuffer);
109
110 return nullptr;
111 }
112
113 /**
114 * destroy this video buffer
115 */
116 void
d3d12_video_buffer_destroy(struct pipe_video_buffer * buffer)117 d3d12_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 */
165 void
d3d12_video_buffer_destroy_associated_data(void * associated_data)166 d3d12_video_buffer_destroy_associated_data(void *associated_data)
167 { }
168
169 /**
170 * get an individual surfaces for each plane
171 */
172 struct pipe_surface **
d3d12_video_buffer_get_surfaces(struct pipe_video_buffer * buffer)173 d3d12_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
209 error:
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 */
220 struct pipe_sampler_view **
d3d12_video_buffer_get_sampler_view_planes(struct pipe_video_buffer * buffer)221 d3d12_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
260 error:
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 */
271 struct pipe_sampler_view **
d3d12_video_buffer_get_sampler_view_components(struct pipe_video_buffer * buffer)272 d3d12_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
321 error:
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