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