2 * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstqsvallocator_d3d11.h"
29 using namespace Microsoft::WRL;
32 GST_DEBUG_CATEGORY_EXTERN (gst_qsv_allocator_debug);
33 #define GST_CAT_DEFAULT gst_qsv_allocator_debug
35 struct _GstQsvD3D11Allocator
37 GstQsvAllocator parent;
39 GstD3D11Device *device;
43 #define gst_qsv_d3d11_allocator_parent_class parent_class
44 G_DEFINE_TYPE (GstQsvD3D11Allocator, gst_qsv_d3d11_allocator,
45 GST_TYPE_QSV_ALLOCATOR);
47 static void gst_qsv_d3d11_allocator_dispose (GObject * object);
48 static mfxStatus gst_qsv_d3d11_allocator_alloc (GstQsvAllocator * allocator,
49 gboolean dummy_alloc, mfxFrameAllocRequest * request,
50 mfxFrameAllocResponse * response);
51 static GstBuffer *gst_qsv_d3d11_allocator_upload (GstQsvAllocator * allocator,
52 const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool);
53 static GstBuffer *gst_qsv_d3d11_allocator_download (GstQsvAllocator * allocator,
54 const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
55 GstBufferPool * pool);
58 gst_qsv_d3d11_allocator_class_init (GstQsvD3D11AllocatorClass * klass)
60 GObjectClass *object_class = G_OBJECT_CLASS (klass);
61 GstQsvAllocatorClass *alloc_class = GST_QSV_ALLOCATOR_CLASS (klass);
63 object_class->dispose = gst_qsv_d3d11_allocator_dispose;
65 alloc_class->alloc = GST_DEBUG_FUNCPTR (gst_qsv_d3d11_allocator_alloc);
66 alloc_class->upload = GST_DEBUG_FUNCPTR (gst_qsv_d3d11_allocator_upload);
67 alloc_class->download = GST_DEBUG_FUNCPTR (gst_qsv_d3d11_allocator_download);
71 gst_qsv_d3d11_allocator_init (GstQsvD3D11Allocator * self)
76 gst_qsv_d3d11_allocator_dispose (GObject * object)
78 GstQsvD3D11Allocator *self = GST_QSV_D3D11_ALLOCATOR (object);
80 gst_clear_d3d11_fence (&self->fence);
81 gst_clear_object (&self->device);
83 G_OBJECT_CLASS (parent_class)->dispose (object);
87 gst_qsv_d3d11_allocator_alloc (GstQsvAllocator * allocator,
88 gboolean dummy_alloc, mfxFrameAllocRequest * request,
89 mfxFrameAllocResponse * response)
91 GstQsvD3D11Allocator *self = GST_QSV_D3D11_ALLOCATOR (allocator);
92 DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
93 GstQsvFrame **mids = nullptr;
95 /* Something unexpected and went wrong */
96 if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) != 0) {
97 GST_ERROR_OBJECT (self,
98 "MFX is requesting system memory, type 0x%x", request->Type);
99 return MFX_ERR_UNSUPPORTED;
102 switch (request->Info.FourCC) {
103 case MFX_FOURCC_NV12:
104 dxgi_format = DXGI_FORMAT_NV12;
106 case MFX_FOURCC_P010:
107 dxgi_format = DXGI_FORMAT_P010;
109 case MFX_FOURCC_AYUV:
110 dxgi_format = DXGI_FORMAT_AYUV;
112 case MFX_FOURCC_Y410:
113 dxgi_format = DXGI_FORMAT_Y410;
115 case MFX_FOURCC_RGB4:
116 dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
118 case MFX_FOURCC_BGR4:
119 dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM;
122 /* TODO: add more formats */
126 if (dxgi_format == DXGI_FORMAT_UNKNOWN &&
127 request->Info.FourCC != MFX_FOURCC_P8) {
128 GST_ERROR_OBJECT (self, "Failed to convert %d to DXGI format",
129 request->Info.FourCC);
131 return MFX_ERR_UNSUPPORTED;
134 if (request->Info.FourCC == MFX_FOURCC_P8) {
135 D3D11_BUFFER_DESC desc;
139 gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
140 gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
143 memset (&desc, 0, sizeof (D3D11_BUFFER_DESC));
145 desc.ByteWidth = request->Info.Width * request->Info.Height;
146 desc.Usage = D3D11_USAGE_STAGING;
147 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
149 mem = gst_d3d11_allocator_alloc_buffer (nullptr, self->device, &desc);
151 GST_ERROR_OBJECT (self, "Failed to allocate buffer");
152 return MFX_ERR_MEMORY_ALLOC;
155 size = request->Info.Width * request->Info.Height;
158 gst_video_info_set_format (&info, GST_VIDEO_FORMAT_GRAY8, size, 1);
160 buffer = gst_buffer_new ();
161 gst_buffer_append_memory (buffer, mem);
162 gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
163 GST_VIDEO_FORMAT_GRAY8, size, 1, 1, offset, stride);
165 mids = g_new0 (GstQsvFrame *, 1);
166 response->NumFrameActual = 1;
167 mids[0] = gst_qsv_allocator_acquire_frame (allocator,
168 GST_QSV_VIDEO_MEMORY | GST_QSV_ENCODER_IN_MEMORY, &info, buffer,
172 GstVideoFormat format;
175 GstStructure *config;
176 GstD3D11AllocationParams *params;
177 guint bind_flags = 0;
178 GstVideoAlignment align;
179 GstQsvMemoryType mem_type = GST_QSV_VIDEO_MEMORY;
181 if ((request->Type & MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET) != 0) {
182 bind_flags |= D3D11_BIND_VIDEO_ENCODER;
183 mem_type |= GST_QSV_ENCODER_IN_MEMORY;
186 if ((request->Type & MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET) != 0) {
187 bind_flags |= D3D11_BIND_DECODER;
188 mem_type |= GST_QSV_DECODER_OUT_MEMORY;
191 if (mem_type == GST_QSV_VIDEO_MEMORY) {
192 GST_ERROR_OBJECT (self, "Unknown read/write access");
193 return MFX_ERR_UNSUPPORTED;
196 mids = g_new0 (GstQsvFrame *, request->NumFrameSuggested);
197 response->NumFrameActual = request->NumFrameSuggested;
199 format = gst_d3d11_dxgi_format_to_gst (dxgi_format);
200 gst_video_info_set_format (&info,
201 format, request->Info.CropW, request->Info.CropH);
204 for (guint i = 0; i < request->NumFrameSuggested; i++) {
205 mids[i] = gst_qsv_allocator_acquire_frame (allocator,
206 mem_type, &info, nullptr, nullptr);
209 response->mids = (mfxMemId *) mids;
214 caps = gst_video_info_to_caps (&info);
215 gst_video_alignment_reset (&align);
216 align.padding_right = request->Info.Width - request->Info.CropW;
217 align.padding_bottom = request->Info.Height - request->Info.CropH;
219 pool = gst_d3d11_buffer_pool_new (self->device);
220 params = gst_d3d11_allocation_params_new (self->device, &info,
221 GST_D3D11_ALLOCATION_FLAG_DEFAULT, bind_flags, 0);
223 gst_d3d11_allocation_params_alignment (params, &align);
225 config = gst_buffer_pool_get_config (pool);
226 gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
227 gst_d3d11_allocation_params_free (params);
228 gst_buffer_pool_config_set_params (config, caps,
229 GST_VIDEO_INFO_SIZE (&info), 0, 0);
230 gst_caps_unref (caps);
231 gst_buffer_pool_set_config (pool, config);
232 gst_buffer_pool_set_active (pool, TRUE);
234 for (guint i = 0; i < request->NumFrameSuggested; i++) {
237 if (gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr) !=
239 GST_ERROR_OBJECT (self, "Failed to allocate texture buffer");
240 gst_buffer_pool_set_active (pool, FALSE);
241 gst_object_unref (pool);
245 mids[i] = gst_qsv_allocator_acquire_frame (allocator,
246 mem_type, &info, buffer, nullptr);
249 gst_buffer_pool_set_active (pool, FALSE);
250 gst_object_unref (pool);
253 response->mids = (mfxMemId *) mids;
259 for (guint i = 0; i < response->NumFrameActual; i++)
260 gst_clear_qsv_frame (&mids[i]);
265 response->NumFrameActual = 0;
267 return MFX_ERR_MEMORY_ALLOC;
271 gst_qsv_frame_copy_d3d11 (GstQsvD3D11Allocator * self,
272 const GstVideoInfo * info, GstBuffer * src_buf, GstBuffer * dst_buf,
275 D3D11_TEXTURE2D_DESC src_desc, dst_desc;
277 guint subresource_idx;
278 GstMemory *src_mem, *dst_mem;
279 GstMapInfo src_info, dst_info;
280 ID3D11Texture2D *src_tex, *dst_tex;
281 GstD3D11Device *device;
282 ID3D11Device *device_handle;
283 ID3D11DeviceContext *device_context;
284 ComPtr < IDXGIResource > dxgi_resource;
285 ComPtr < ID3D11Texture2D > shared_texture;
286 HANDLE shared_handle;
289 GST_TRACE_OBJECT (self, "Copying D3D11 buffer %" GST_PTR_FORMAT, src_buf);
291 src_mem = gst_buffer_peek_memory (src_buf, 0);
292 dst_mem = gst_buffer_peek_memory (dst_buf, 0);
294 device = GST_D3D11_MEMORY_CAST (src_mem)->device;
295 device_handle = gst_d3d11_device_get_device_handle (device);
296 device_context = gst_d3d11_device_get_device_context_handle (device);
298 if (!gst_memory_map (src_mem,
299 &src_info, (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
300 GST_WARNING ("Failed to map src memory");
301 gst_buffer_unref (dst_buf);
305 if (!gst_memory_map (dst_mem,
306 &dst_info, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
307 GST_WARNING ("Failed to map dst memory");
308 gst_memory_unmap (src_mem, &src_info);
309 gst_buffer_unref (dst_buf);
313 src_tex = (ID3D11Texture2D *) src_info.data;
314 dst_tex = (ID3D11Texture2D *) dst_info.data;
316 src_tex->GetDesc (&src_desc);
317 dst_tex->GetDesc (&dst_desc);
320 gst_d3d11_memory_get_subresource_index (GST_D3D11_MEMORY_CAST (src_mem));
323 hr = dst_tex->QueryInterface (IID_PPV_ARGS (&dxgi_resource));
324 if (!gst_d3d11_result (hr, device)) {
325 GST_ERROR_OBJECT (self,
326 "IDXGIResource interface is not available, hr: 0x%x", (guint) hr);
330 hr = dxgi_resource->GetSharedHandle (&shared_handle);
331 if (!gst_d3d11_result (hr, device)) {
332 GST_ERROR_OBJECT (self, "Failed to get shared handle, hr: 0x%x",
337 hr = device_handle->OpenSharedResource (shared_handle,
338 IID_PPV_ARGS (&shared_texture));
340 if (!gst_d3d11_result (hr, device)) {
341 GST_ERROR_OBJECT (self, "Failed to get shared texture, hr: 0x%x",
346 dst_tex = shared_texture.Get ();
353 src_box.right = MIN (src_desc.Width, dst_desc.Width);
354 src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
356 gst_d3d11_device_lock (device);
358 if (self->fence && self->fence->device != device)
359 gst_clear_d3d11_fence (&self->fence);
362 self->fence = gst_d3d11_device_create_fence (device);
365 GST_ERROR_OBJECT (self, "Couldn't crete fence");
366 gst_d3d11_device_unlock (device);
371 device_context->CopySubresourceRegion (dst_tex, 0,
372 0, 0, 0, src_tex, subresource_idx, &src_box);
375 if (!gst_d3d11_fence_signal (self->fence) ||
376 !gst_d3d11_fence_wait (self->fence)) {
377 GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
378 gst_clear_d3d11_fence (&self->fence);
379 gst_d3d11_device_unlock (device);
384 gst_d3d11_device_unlock (device);
386 gst_memory_unmap (dst_mem, &dst_info);
387 gst_memory_unmap (src_mem, &src_info);
392 gst_memory_unmap (dst_mem, &dst_info);
393 gst_memory_unmap (src_mem, &src_info);
394 gst_buffer_unref (dst_buf);
400 gst_qsv_frame_upload_sysmem (const GstVideoInfo * info, GstBuffer * src_buf,
403 GstVideoFrame src_frame, dst_frame;
405 GST_TRACE ("Uploading sysmem buffer %" GST_PTR_FORMAT, src_buf);
407 if (!gst_video_frame_map (&src_frame, info, src_buf, GST_MAP_READ)) {
408 GST_WARNING ("Failed to map src frame");
409 gst_buffer_unref (dst_buf);
413 if (!gst_video_frame_map (&dst_frame, info, dst_buf, GST_MAP_WRITE)) {
414 GST_WARNING ("Failed to map src frame");
415 gst_video_frame_unmap (&src_frame);
416 gst_buffer_unref (dst_buf);
420 for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&src_frame); i++) {
421 guint src_width_in_bytes, src_height;
422 guint dst_width_in_bytes, dst_height;
423 guint width_in_bytes, height;
424 guint src_stride, dst_stride;
425 guint8 *src_data, *dst_data;
427 src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
428 GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
429 src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
430 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (&src_frame, i);
432 dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
433 GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
434 dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
435 dst_stride = GST_VIDEO_FRAME_COMP_STRIDE (&dst_frame, i);
437 width_in_bytes = MIN (src_width_in_bytes, dst_width_in_bytes);
438 height = MIN (src_height, dst_height);
440 src_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
441 dst_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
443 for (guint j = 0; j < height; j++) {
444 memcpy (dst_data, src_data, width_in_bytes);
445 dst_data += dst_stride;
446 src_data += src_stride;
450 gst_video_frame_unmap (&dst_frame);
451 gst_video_frame_unmap (&src_frame);
457 gst_qsv_d3d11_allocator_upload (GstQsvAllocator * allocator,
458 const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
461 GstD3D11Memory *dmem;
462 D3D11_TEXTURE2D_DESC desc;
464 GstFlowReturn flow_ret;
465 gboolean shared_copy = FALSE;
467 /* 1) D3D11 buffer from the same d3d11device with ours
468 * 1-1) Same resolution
469 * -> Increase refcount and wrap with GstQsvFrame
470 * 1-2) Different resolution
472 * 2) non-D3D11 buffer or from other d3d11 device
476 if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
477 GST_ERROR_OBJECT (allocator, "Not a d3d11 buffer pool");
481 flow_ret = gst_buffer_pool_acquire_buffer (pool, &dst_buf, nullptr);
482 if (flow_ret != GST_FLOW_OK) {
483 GST_WARNING_OBJECT (allocator,
484 "Failed to acquire buffer from pool, return %s",
485 gst_flow_get_name (flow_ret));
489 mem = gst_buffer_peek_memory (buffer, 0);
490 if (!gst_is_d3d11_memory (mem) || gst_buffer_n_memory (buffer) > 1) {
491 /* d3d11 buffer should hold single memory object */
492 return gst_qsv_frame_upload_sysmem (info, buffer, dst_buf);
495 /* FIXME: Add support for shared texture for GPU copy or wrapping
496 * texture from different device */
497 dmem = GST_D3D11_MEMORY_CAST (mem);
498 if (dmem->device != GST_D3D11_BUFFER_POOL (pool)->device) {
499 gint64 luid, other_luid;
500 g_object_get (dmem->device, "adapter-luid", &luid, nullptr);
501 g_object_get (GST_D3D11_BUFFER_POOL (pool)->device,
502 "adapter-luid", &other_luid, nullptr);
503 if (luid == other_luid) {
506 return gst_qsv_frame_upload_sysmem (info, buffer, dst_buf);
510 gst_d3d11_memory_get_texture_desc (dmem, &desc);
512 if (desc.Usage == D3D11_USAGE_DEFAULT && !shared_copy) {
513 GST_TRACE_OBJECT (allocator, "Wrapping D3D11 buffer without copy");
514 gst_buffer_unref (dst_buf);
516 return gst_buffer_ref (buffer);
519 return gst_qsv_frame_copy_d3d11 (GST_QSV_D3D11_ALLOCATOR (allocator), info,
520 buffer, dst_buf, shared_copy);
524 gst_qsv_d3d11_allocator_download (GstQsvAllocator * allocator,
525 const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
526 GstBufferPool * pool)
528 GstD3D11BufferPool *d3d11_pool;
529 GstBuffer *src_buf, *dst_buf;
531 GstD3D11Memory *dmem;
534 GST_TRACE_OBJECT (allocator, "Download");
536 src_buf = gst_qsv_frame_peek_buffer (frame);
539 return gst_buffer_ref (src_buf);
541 mem = gst_buffer_peek_memory (src_buf, 0);
542 if (!gst_is_d3d11_memory (mem) || gst_buffer_n_memory (src_buf) != 1) {
543 GST_ERROR_OBJECT (allocator, "frame holds invalid d3d11 memory");
547 if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
548 GST_TRACE_OBJECT (allocator, "Output is not d3d11 memory");
552 dmem = GST_D3D11_MEMORY_CAST (mem);
554 /* both pool and qsvframe should hold the same d3d11 device already,
555 * but checking again */
556 d3d11_pool = GST_D3D11_BUFFER_POOL (pool);
557 if (d3d11_pool->device != dmem->device) {
558 GST_WARNING_OBJECT (allocator, "Pool holds different device");
562 ret = gst_buffer_pool_acquire_buffer (pool, &dst_buf, nullptr);
563 if (ret != GST_FLOW_OK) {
564 GST_WARNING_OBJECT (allocator, "Failed to allocate output buffer");
568 return gst_qsv_frame_copy_d3d11 (GST_QSV_D3D11_ALLOCATOR (allocator),
569 info, src_buf, dst_buf, FALSE);
572 GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
574 return GST_QSV_ALLOCATOR_CLASS (parent_class)->download (allocator,
575 info, TRUE, frame, pool);
579 gst_qsv_d3d11_allocator_new (GstD3D11Device * device)
581 GstQsvD3D11Allocator *self;
583 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
585 self = (GstQsvD3D11Allocator *)
586 g_object_new (GST_TYPE_QSV_D3D11_ALLOCATOR, nullptr);
587 self->device = (GstD3D11Device *) gst_object_ref (device);
589 gst_object_ref_sink (self);
591 return GST_QSV_ALLOCATOR (self);