blob: 5fbb24f858a2b8f5899263da66f01633f0c6828f [file] [log] [blame]
/*
* # Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/allocators/gstdmabuf.h>
#include <gst/gl/gstglfuncs.h>
#include "gstglmemorydma.h"
GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_MEMORY_DMA);
#define GST_CAT_DEFAULT GST_CAT_GL_MEMORY_DMA
#define parent_class gst_gl_memory_dma_allocator_parent_class
G_DEFINE_TYPE (GstGLMemoryDmaAllocator, gst_gl_memory_dma_allocator,
GST_TYPE_GL_MEMORY_ALLOCATOR);
static void gst_gl_memory_dma_allocator_finalize (GObject * object);
static GstGLBaseMemory * _gl_mem_dma_alloc
(GstGLBaseMemoryAllocator * allocator, GstGLAllocationParams * params);
static gboolean _gl_mem_dma_create (GstGLBaseMemory * glmem, GError ** error);
static void _gl_mem_dma_destroy (GstGLBaseMemory * glmem);
static GstMemory * _mem_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params);
static void
gst_gl_memory_dma_allocator_class_init (GstGLMemoryDmaAllocatorClass * klass)
{
GST_ALLOCATOR_CLASS (klass)->alloc = _mem_alloc;
GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->alloc = _gl_mem_dma_alloc;
GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->create = _gl_mem_dma_create;
GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->destroy = _gl_mem_dma_destroy;
G_OBJECT_CLASS (klass)->finalize = gst_gl_memory_dma_allocator_finalize;
GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_MEMORY_DMA, "glmemorydma", 0,
"OpenGL DMA memory");
}
static void
gst_gl_memory_dma_allocator_init (GstGLMemoryDmaAllocator * alloc)
{
GST_DEBUG_OBJECT (alloc, "init");
GST_ALLOCATOR (alloc)->mem_type = GST_GL_MEMORY_DMA_ALLOCATOR_NAME;
GST_OBJECT_FLAG_SET (GST_OBJECT (alloc), GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
/* ION allocator is in the "coral" plugin, load it. */
alloc->plugin = gst_plugin_load_by_name ("coral");
if (!alloc->plugin) {
GST_ERROR_OBJECT (alloc, "coral plugin not found");
return;
}
alloc->ion_allocator = gst_allocator_find ("ion");
if (!alloc->ion_allocator) {
GST_ERROR_OBJECT (alloc, "ION allocator not found");
return;
}
GST_INFO_OBJECT (alloc, "ION allocator loaded");
}
static void
gst_gl_memory_dma_allocator_finalize (GObject * object)
{
GstGLMemoryDmaAllocator *alloc = GST_GL_MEMORY_DMA_ALLOCATOR (object);
GST_DEBUG_OBJECT (alloc, "finalize");
if (alloc->ion_allocator) {
gst_object_unref (alloc->ion_allocator);
alloc->ion_allocator = NULL;
}
if (alloc->plugin) {
gst_object_unref (alloc->plugin);
alloc->plugin = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstGLBaseMemory *
_gl_mem_dma_alloc (GstGLBaseMemoryAllocator * allocator,
GstGLAllocationParams * alloc_params)
{
GstGLMemoryDmaAllocator *alloc;
GstGLMemoryDMA *dmamem;
GstGLVideoAllocationParams *params;
gsize size;
alloc = GST_GL_MEMORY_DMA_ALLOCATOR (allocator);
params = (GstGLVideoAllocationParams *) alloc_params;
g_return_val_if_fail (params->parent.alloc_flags &
GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, NULL);
dmamem = g_new0 (GstGLMemoryDMA, 1);
dmamem->params = params->parent.alloc_params;
/* GstVideoInfo defaults to 4 b stride align for RGB/BGR but we want to
* pack the buffer as tightly as possible (i.e. stride == width * bpp).
*/
switch (params->v_info->finfo->format) {
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
params->v_info->stride[0] = params->v_info->width * 3;
params->v_info->size = params->v_info->stride[0] * params->v_info->height;
break;
default:
break;
}
size = gst_gl_get_plane_data_size (params->v_info, params->valign,
params->plane);
dmamem->dma = gst_allocator_alloc (alloc->ion_allocator, size,
dmamem->params);
if (!dmamem->dma) {
GST_CAT_ERROR (GST_CAT_GL_MEMORY_DMA, "ION alloc of %zu b failed", size);
g_free (dmamem);
return NULL;
}
GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "ION alloc %zu b", size);
gst_gl_memory_init (GST_GL_MEMORY_CAST (dmamem), GST_ALLOCATOR_CAST (alloc),
NULL, params->parent.context, params->target, params->tex_format,
params->parent.alloc_params, params->v_info, params->plane,
params->valign, params->parent.user_data, params->parent.notify);
return GST_GL_BASE_MEMORY_CAST (dmamem);
}
static gboolean
_gl_mem_dma_create (GstGLBaseMemory * glmem, GError ** error)
{
GstGLMemoryDMA *dmamem;
const GstGLFuncs *gl;
gint fd;
guint tex_id;
g_return_val_if_fail (glmem, FALSE);
g_return_val_if_fail (glmem->context, FALSE);
gl = glmem->context->gl_vtable;
if (!GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->create (glmem,
error)) {
return FALSE;
}
dmamem = GST_GL_MEMORY_DMA_CAST (glmem);
fd = gst_dmabuf_memory_get_fd (dmamem->dma);
tex_id = GST_GL_MEMORY_CAST (&glmem->mem)->tex_id;
dmamem->image = gst_egl_image_from_dmabuf (glmem->context, fd,
&GST_GL_MEMORY_CAST (glmem)->info, 0, 0);
if (!dmamem->image) {
GST_CAT_ERROR (GST_CAT_GL_MEMORY_DMA, "gst_egl_image_from_dmabuf failed");
return FALSE;
}
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (GL_TEXTURE_2D, tex_id);
gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, gst_egl_image_get_image (
dmamem->image));
GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "created dma mem %p fd %d tex_id %u",
glmem, fd, tex_id);
return TRUE;
}
static void
_gl_mem_dma_destroy (GstGLBaseMemory * glmem)
{
GstGLMemoryDMA *dmamem;
GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->destroy (glmem);
dmamem = GST_GL_MEMORY_DMA_CAST (glmem);
if (dmamem->image) {
gst_egl_image_unref (dmamem->image);
dmamem->image = NULL;
}
if (dmamem->dma) {
gst_memory_unref (dmamem->dma);
dmamem->dma = NULL;
}
/* Base class frees dmamem. */
GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->destroy (glmem);
GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "destroyed dma mem %p", dmamem);
}
static GstMemory *
_mem_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
/* Only used as an init check, does not support actual GL alloc. */
GstGLMemoryDmaAllocator *alloc;
alloc = GST_GL_MEMORY_DMA_ALLOCATOR (allocator);
g_return_val_if_fail (alloc->ion_allocator, NULL);
return gst_allocator_alloc (alloc->ion_allocator, size, params);
}
static EGLDisplay
_get_egl_display (GstGLContext * context)
{
EGLDisplay egl_display;
GstGLDisplayEGL *display_egl;
if (!context->display) {
return EGL_NO_DISPLAY;
}
display_egl = gst_gl_display_egl_from_gl_display (context->display);
egl_display =
(EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl));
gst_object_unref (display_egl);
return egl_display;
}
static EGLSyncKHR
_eglCreateSyncKHR (GstGLContext * context)
{
GstClockTime ts;
EGLSyncKHR sync;
EGLSyncKHR (*gst_eglCreateSyncKHR) (EGLDisplay dpy, EGLenum type,
const EGLAttrib * attrib_list);
EGLDisplay dpy = _get_egl_display (context);
g_assert (dpy != EGL_NO_DISPLAY);
ts = gst_util_get_timestamp ();
gst_eglCreateSyncKHR = gst_gl_context_get_proc_address (context,
"eglCreateSyncKHR");
sync = gst_eglCreateSyncKHR (dpy, EGL_SYNC_FENCE_KHR, NULL);
GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "created egl sync object %p", sync);
ts = gst_util_get_timestamp () - ts;
GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "create %.2g ms",(double) ts / GST_MSECOND);
return sync;
}
static EGLBoolean
_eglClientWaitSyncKHR (GstGLContext * context, EGLSyncKHR sync)
{
EGLint (*gst_eglClientWaitSyncKHR) (EGLDisplay dpy, EGLSyncKHR sync,
EGLint flags, EGLTimeKHR timeout);
EGLDisplay dpy = _get_egl_display (context);
g_assert (dpy != EGL_NO_DISPLAY);
GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "waiting on egl sync object %p", sync);
gst_eglClientWaitSyncKHR = gst_gl_context_get_proc_address (context,
"eglClientWaitSyncKHR");
return gst_eglClientWaitSyncKHR (dpy, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
1000000000 /* 1s */ ) == EGL_CONDITION_SATISFIED_KHR;
}
static EGLBoolean
_eglDestroySyncKHR (GstGLContext * context, EGLSyncKHR sync)
{
EGLBoolean (*gst_eglDestroySyncKHR) (EGLDisplay dpy, EGLSyncKHR sync);
EGLDisplay dpy = _get_egl_display (context);
if (dpy == EGL_NO_DISPLAY) {
return EGL_FALSE;
}
gst_eglDestroySyncKHR = gst_gl_context_get_proc_address (context,
"eglDestroySyncKHR");
GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "deleting egl sync object %p", sync);
return gst_eglDestroySyncKHR (dpy, sync);
}
static void
_create_sync_gl (GstGLContext * context, gpointer data)
{
EGLSyncKHR *sync = (EGLSyncKHR *) data;
*sync = _eglCreateSyncKHR (context);
}
GstBuffer *
gst_gl_memory_dma_sync_buffer (GstBuffer * glbuf)
{
GstBuffer *buf;
GstVideoInfo *info;
GstMemory *mem;
GstGLBaseMemory *glmem;
GstGLMemoryDMA *dmamem;
GstGLSyncMeta *sync_meta;
EGLSyncKHR sync;
EGLBoolean res;
GstClockTime ts;
mem = gst_buffer_peek_memory (glbuf, 0);
g_assert (gst_is_gl_memory_dma (mem));
glmem = GST_GL_BASE_MEMORY_CAST (mem);
dmamem = GST_GL_MEMORY_DMA_CAST (mem);
sync_meta = gst_buffer_get_gl_sync_meta (glbuf);
if (sync_meta) {
ts = gst_util_get_timestamp ();
gst_gl_sync_meta_wait_cpu (sync_meta, glmem->context);
ts = gst_util_get_timestamp () - ts;
GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "wait_cpu %.2g ms",
(double) ts / GST_MSECOND);
} else {
ts = gst_util_get_timestamp ();
gst_gl_context_thread_add (glmem->context, _create_sync_gl, &sync);
g_assert (sync != EGL_NO_SYNC_KHR);
do {
res = _eglClientWaitSyncKHR (glmem->context, sync);
} while (res == EGL_FALSE);
ts = gst_util_get_timestamp () - ts;
res = _eglDestroySyncKHR (glmem->context, sync);
g_assert (res == EGL_TRUE);
ts = gst_util_get_timestamp () - ts;
GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "egl_sync %.2g ms",
(double) ts / GST_MSECOND);
}
info = &dmamem->mem.info;
buf = gst_buffer_new ();
gst_buffer_add_parent_buffer_meta (buf, glbuf);
gst_buffer_append_memory (buf, gst_memory_ref (dmamem->dma));
gst_buffer_add_video_meta_full (buf, 0,
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
GST_VIDEO_INFO_HEIGHT (info), 1, info->offset, info->stride);
return buf;
}
gboolean
gst_is_gl_memory_dma (GstMemory * mem)
{
return mem && mem->allocator && g_type_is_a (G_OBJECT_TYPE (mem->allocator),
GST_TYPE_GL_MEMORY_DMA_ALLOCATOR);
}
void
gst_gl_memory_dma_init_once (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GstAllocator *alloc = g_object_new (GST_TYPE_GL_MEMORY_DMA_ALLOCATOR, NULL);
gst_object_ref_sink (alloc);
gst_allocator_register (GST_GL_MEMORY_DMA_ALLOCATOR_NAME, alloc);
g_once_init_leave (&_init, 1);
}
}