/* * Derived from GStreamer 0.10 EGL Library in gst-omx * Copyright (C) 2014 Fluendo S.A. * @author: Josep Torra * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define EGL_EGLEXT_PROTOTYPES #include "egl.h" #define DEBUG_MAGIC_CHECK 0 #if DEBUG_MAGIC_CHECK #define EGLMEMORY_MAGIC 0x5fce5fce #define EGLMEMORY_POOL_MAGIC 0x5fce9001 #define EGLMEMORY_POOL_MAGIC_ASSERT(pool) G_STMT_START { \ g_assert (pool->magic == EGLMEMORY_POOL_MAGIC); \ } G_STMT_END #define EGLMEMORY_MAGIC_ASSERT(mem) G_STMT_START { \ g_assert (mem->magic == EGLMEMORY_MAGIC); \ } G_STMT_END #else #define EGLMEMORY_POOL_MAGIC_ASSERT(pool) G_STMT_START{ }G_STMT_END #define EGLMEMORY_MAGIC_ASSERT(mem) G_STMT_START{ }G_STMT_END #endif struct _EGLImageMemory { volatile gint refcount; #if DEBUG_MAGIC_CHECK guint32 magic; #endif EGLClientBuffer client_buffer; EGLImageKHR image; gpointer user_data; GDestroyNotify destroy_data; EGLImageMemoryPool *pool; }; #define EGL_IMAGE_MEMORY(mem) ((EGLImageMemory *)(mem)) struct _EGLImageMemoryPool { volatile gint refcount; #if DEBUG_MAGIC_CHECK guint32 magic; #endif GMutex *lock; GCond *cond; EGLDisplayWrapper *display; EGLImageMemory *memory; gint size; volatile gint unused; volatile gint active; gpointer user_data; DestroyNotifyEGLImageMemoryPool destroy_data; }; #define EGL_IMAGE_MEMORY_POOL_LOCK(pool) G_STMT_START { \ g_mutex_lock (pool->lock); \ } G_STMT_END #define EGL_IMAGE_MEMORY_POOL_UNLOCK(pool) G_STMT_START { \ g_mutex_unlock (pool->lock); \ } G_STMT_END #define EGL_IMAGE_MEMORY_POOL_WAIT_RELEASED(pool) G_STMT_START { \ g_cond_wait (pool->cond, pool->lock); \ } G_STMT_END #define EGL_IMAGE_MEMORY_POOL_SIGNAL_UNUSED(pool) G_STMT_START { \ g_cond_signal (pool->cond); \ } G_STMT_END #define EGL_IMAGE_MEMORY_POOL_IS_FULL(pool) (g_atomic_int_get (&pool->unused) == 0) #define EGL_IMAGE_MEMORY_POOL_IS_EMPTY(pool) (g_atomic_int_get (&pool->unused) == pool->size) #define EGL_IMAGE_MEMORY_POOL_SET_ACTIVE(pool,active) \ g_atomic_int_set (&pool->active, active) #define EGL_IMAGE_MEMORY_POOL_IS_ACTIVE(pool) (g_atomic_int_get (&pool->active) == TRUE) struct _EGLDisplayWrapper { volatile gint refcount; EGLDisplay display; gpointer user_data; GDestroyNotify destroy_data; }; /** * egl_image_memory_ref * @mem: a #EGLImageMemory * * Increase the refcount of @mem. * */ EGLImageMemory * egl_image_memory_ref (EGLImageMemory * mem) { g_return_val_if_fail (mem != NULL, NULL); EGLMEMORY_MAGIC_ASSERT (mem); g_atomic_int_inc (&mem->refcount); return mem; } static void egl_image_memory_free (EGLImageMemory * mem) { EGLImageMemoryPool *pool = mem->pool; /* We grab the pool lock as we will make changes to number of unused * memories. */ EGL_IMAGE_MEMORY_POOL_LOCK (pool); /* We have one more unused memory */ g_atomic_int_inc (&pool->unused); /* Wake up potential waiters */ EGL_IMAGE_MEMORY_POOL_SIGNAL_UNUSED (pool); EGL_IMAGE_MEMORY_POOL_UNLOCK (pool); if (mem->destroy_data) { mem->destroy_data (mem->user_data); } /* Remove our ref to the pool. That could destroy the pool so it is * important to do that when we released the lock */ mem->pool = egl_image_memory_pool_unref (pool); } /** * egl_image_memory_unref: * @mem: a #EGLImageMemory * * Unref @mem and when the refcount reaches 0 frees the resources * and return NULL. * */ EGLImageMemory * egl_image_memory_unref (EGLImageMemory * mem) { g_return_val_if_fail (mem != NULL, NULL); EGLMEMORY_MAGIC_ASSERT (mem); if (g_atomic_int_dec_and_test (&mem->refcount)) { egl_image_memory_free (mem); mem = NULL; } return mem; } /** * egl_image_memory_get_image: * @mem: a #EGLImageMemory * * Gives the #EGLImageKHR used by the #EGLImageMemory. * */ EGLImageKHR egl_image_memory_get_image (EGLImageMemory * mem) { return mem->image; } /** * egl_image_memory_pool_new: * @size: a number of memories managed by the pool * @display: a #EGLDisplayWrapper display * @user_data: user data passed to the callback * @destroy_data: #DestroyNotifyEGLImageMemoryPool for user_data * * Create a new EGLImageMemoryPool instance with the provided images. * * Returns: a new #EGLImageMemoryPool * */ EGLImageMemoryPool * egl_image_memory_pool_new (gint size, EGLDisplayWrapper * display, gpointer user_data, DestroyNotifyEGLImageMemoryPool destroy_data) { EGLImageMemoryPool *pool; pool = g_new0 (EGLImageMemoryPool, 1); pool->refcount = 1; pool->lock = g_mutex_new (); pool->cond = g_cond_new (); pool->display = egl_display_ref (display); pool->memory = (EGLImageMemory *) g_new0 (EGLImageMemory, size); pool->size = pool->unused = size; pool->user_data = user_data; pool->destroy_data = destroy_data; #if DEBUG_MAGIC_CHECK { gint i; pool->magic = EGLMEMORY_POOL_MAGIC; for (i = 0; i < size; i++) { EGLImageMemory *mem = &pool->memory[i]; mem->magic = EGLMEMORY_MAGIC; } } #endif return pool; } /** * egl_image_memory_pool_ref: * @pool: a #EGLImageMemoryPool * * Increase the refcount of @pool. * */ EGLImageMemoryPool * egl_image_memory_pool_ref (EGLImageMemoryPool * pool) { g_return_val_if_fail (pool != NULL, NULL); EGLMEMORY_POOL_MAGIC_ASSERT (pool); g_atomic_int_inc (&pool->refcount); return pool; } static void egl_image_memory_pool_free (EGLImageMemoryPool * pool) { if (g_atomic_int_get (&pool->unused) != pool->size) { g_critical ("refcounting problem detected, some memory is still in use"); } if (pool->destroy_data) { pool->destroy_data (pool, pool->user_data); } egl_display_unref (pool->display); g_mutex_free (pool->lock); g_cond_free (pool->cond); g_free (pool->memory); g_free (pool); } /** * egl_image_memory_pool_unref: * @pool: a #EGLImageMemoryPool * * Unref @pool and when the refcount reaches 0 frees the resources * and return NULL. * */ EGLImageMemoryPool * egl_image_memory_pool_unref (EGLImageMemoryPool * pool) { g_return_val_if_fail (pool != NULL, NULL); EGLMEMORY_POOL_MAGIC_ASSERT (pool); if (g_atomic_int_dec_and_test (&pool->refcount)) { egl_image_memory_pool_free (pool); pool = NULL; } return pool; } /** * egl_image_memory_pool_get_size: * @pool: a #EGLImageMemoryPool * * Gives the number of memories that are managed by the pool. * */ gint egl_image_memory_pool_get_size (EGLImageMemoryPool * pool) { g_return_val_if_fail (pool != NULL, 0); return pool->size; } /** * egl_image_memory_pool_set_resources: * @pool: a #EGLImageMemoryPool * @idx: memory index * @client_buffer: an #EGLClientBuffer to store in the pool * @image: an #EGLImageKHR to store in the pool. * * Stores @client_buffer and @image at @idx memory slot. * */ gboolean egl_image_memory_pool_set_resources (EGLImageMemoryPool * pool, gint idx, EGLClientBuffer client_buffer, EGLImageKHR image) { EGLImageMemory *mem; g_return_val_if_fail (pool != NULL, FALSE); g_return_val_if_fail (idx >= 0 && idx < pool->size, FALSE); mem = &pool->memory[idx]; mem->client_buffer = client_buffer; mem->image = image; return TRUE; } /** * egl_image_memory_pool_get_resources: * @pool: a #EGLImageMemoryPool * @idx: memory index * @client_buffer: (out) (allow-none): the #EGLClientBuffer at @idx * @image: (out) (allow-none): the #EGLImageKHR at @idx * * Retrieves @client_buffer and @image at @idx memory slot. * */ gboolean egl_image_memory_pool_get_resources (EGLImageMemoryPool * pool, gint idx, EGLClientBuffer * client_buffer, EGLImageKHR * image) { EGLImageMemory *mem; g_return_val_if_fail (pool != NULL, FALSE); g_return_val_if_fail (idx >= 0 && idx < pool->size, FALSE); mem = &pool->memory[idx]; if (client_buffer) *client_buffer = mem->client_buffer; if (image) *image = mem->image; return TRUE; } /** * egl_image_memory_pool_get_display: * @pool: a #EGLImageMemoryPool * * Provides a reference to the #EGLDisplayWrapper used by the pool */ EGLDisplayWrapper * egl_image_memory_pool_get_display (EGLImageMemoryPool * pool) { g_return_val_if_fail (pool != NULL, NULL); EGLMEMORY_POOL_MAGIC_ASSERT (pool); g_return_val_if_fail (pool->display != NULL, NULL); return egl_display_ref (pool->display); } /** * egl_image_memory_pool_get_images: * @pool: a #EGLImageMemoryPool * * Provides a #GList of EGL images */ GList * egl_image_memory_pool_get_images (EGLImageMemoryPool * pool) { gint i; GList *images = NULL; g_return_val_if_fail (pool != NULL, NULL); for (i = 0; i < pool->size; i++) { EGLImageMemory *mem = &pool->memory[i]; images = g_list_append (images, mem->image); } return images; } /** * egl_image_memory_pool_set_active: * @pool: a #EGLImageMemoryPool * @active: a #gboolean that indicates the pool is active * * Change @pool active state and unblocks any wait condition if needed. * */ void egl_image_memory_pool_set_active (EGLImageMemoryPool * pool, gboolean active) { g_return_if_fail (pool != NULL); EGLMEMORY_POOL_MAGIC_ASSERT (pool); EGL_IMAGE_MEMORY_POOL_SET_ACTIVE (pool, active); if (!active) { EGL_IMAGE_MEMORY_POOL_SIGNAL_UNUSED (pool); } } /** * egl_image_memory_pool_wait_released: * @pool: a #EGLImageMemoryPool * * Waits until none of the memory is in use. * */ void egl_image_memory_pool_wait_released (EGLImageMemoryPool * pool) { g_return_if_fail (pool != NULL); EGLMEMORY_POOL_MAGIC_ASSERT (pool); EGL_IMAGE_MEMORY_POOL_LOCK (pool); while (!EGL_IMAGE_MEMORY_POOL_IS_EMPTY (pool)) { EGL_IMAGE_MEMORY_POOL_WAIT_RELEASED (pool); } EGL_IMAGE_MEMORY_POOL_UNLOCK (pool); } /** * egl_image_memory_pool_acquire_memory: * @pool: a #EGLImageMemoryPool * @idx: ordinal that specifies a #EGLImageMemory in the pool * @user_data: user data passed to the callback * @destroy_data: #GDestroyNotify for user_data * * Provides an specified #EGLImageMemory. * */ EGLImageMemory * egl_image_memory_pool_acquire_memory (EGLImageMemoryPool * pool, gint idx, gpointer user_data, GDestroyNotify destroy_data) { EGLImageMemory *mem; g_return_val_if_fail (pool != NULL, NULL); g_return_val_if_fail (idx >= 0 && idx < pool->size, NULL); EGL_IMAGE_MEMORY_POOL_LOCK (pool); if (!EGL_IMAGE_MEMORY_POOL_IS_ACTIVE (pool)) { EGL_IMAGE_MEMORY_POOL_UNLOCK (pool); return NULL; } mem = &pool->memory[idx]; g_atomic_int_add (&pool->unused, -1); g_atomic_int_set (&mem->refcount, 1); mem->pool = egl_image_memory_pool_ref (pool); mem->user_data = user_data; mem->destroy_data = destroy_data; EGL_IMAGE_MEMORY_POOL_UNLOCK (pool); return mem; } G_DEFINE_BOXED_TYPE (EGLImageMemoryPool, egl_image_memory_pool, (GBoxedCopyFunc) egl_image_memory_pool_ref, (GBoxedFreeFunc) egl_image_memory_pool_unref); /** * egl_display_new: * @display: a #EGLDisplayWrapper display * @user_data: user data passed to the callback * @destroy_data: #GDestroyNotify for user_data * * Create a new #EGLDisplayWrapper that wraps and refcount @display. * * Returns: a new #EGLDisplayWrapper * */ EGLDisplayWrapper * egl_display_new (EGLDisplay display, gpointer user_data, GDestroyNotify destroy_data) { EGLDisplayWrapper *gdisplay; gdisplay = g_slice_new (EGLDisplayWrapper); gdisplay->refcount = 1; gdisplay->display = display; gdisplay->user_data = user_data; gdisplay->destroy_data = destroy_data; return gdisplay; } /** * egl_display_ref * @display: a #EGLDisplayWrapper * * Increase the refcount of @display. * */ EGLDisplayWrapper * egl_display_ref (EGLDisplayWrapper * display) { g_return_val_if_fail (display != NULL, NULL); g_atomic_int_inc (&display->refcount); return display; } /** * egl_display_unref: * @display: a #EGLDisplayWrapper * * Decrease the refcount of @display and calls provided destroy function on * last reference. * */ void egl_display_unref (EGLDisplayWrapper * display) { g_return_if_fail (display != NULL); if (g_atomic_int_dec_and_test (&display->refcount)) { if (display->destroy_data) { display->destroy_data (display->user_data); } g_slice_free (EGLDisplayWrapper, display); } } /** * egl_display_get * @display: a #EGLDisplayWrapper * * Gives the #EGLDisplay wrapped. * */ EGLDisplay egl_display_get (EGLDisplayWrapper * display) { g_return_val_if_fail (display != NULL, EGL_NO_DISPLAY); return display->display; }