diff options
Diffstat (limited to 'Source/DirectFB/src/core/surface_pool.c')
-rwxr-xr-x | Source/DirectFB/src/core/surface_pool.c | 1263 |
1 files changed, 1263 insertions, 0 deletions
diff --git a/Source/DirectFB/src/core/surface_pool.c b/Source/DirectFB/src/core/surface_pool.c new file mode 100755 index 0000000..0518df2 --- /dev/null +++ b/Source/DirectFB/src/core/surface_pool.c @@ -0,0 +1,1263 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp <dok@directfb.org>, + Andreas Hundt <andi@fischlustig.de>, + Sven Neumann <neo@directfb.org>, + Ville Syrjälä <syrjala@sci.fi> and + Claudio Ciccani <klan@users.sf.net>. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <directfb.h> +#include <directfb_util.h> + +#include <direct/debug.h> +#include <direct/mem.h> + +#include <fusion/shmalloc.h> + +#include <core/core.h> +#include <core/coredefs.h> + +#include <core/surface_buffer.h> +#include <core/surface_pool.h> +#include <core/system.h> + +#include <gfx/convert.h> + +#include <misc/conf.h> + + +D_DEBUG_DOMAIN( Core_SurfacePool, "Core/SurfacePool", "DirectFB Core Surface Pool" ); +D_DEBUG_DOMAIN( Core_SurfPoolLock, "Core/SurfPoolLock", "DirectFB Core Surface Pool Lock" ); + +/**********************************************************************************************************************/ + +static const SurfacePoolFuncs *pool_funcs[MAX_SURFACE_POOLS]; +static void *pool_locals[MAX_SURFACE_POOLS]; +static int pool_count; +static CoreSurfacePool *pool_array[MAX_SURFACE_POOLS]; +static unsigned int pool_order[MAX_SURFACE_POOLS]; + +/**********************************************************************************************************************/ + +static inline const SurfacePoolFuncs * +get_funcs( const CoreSurfacePool *pool ) +{ + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_ASSERT( pool->pool_id >= 0 ); + D_ASSERT( pool->pool_id < MAX_SURFACE_POOLS ); + D_ASSERT( pool_funcs[pool->pool_id] != NULL ); + + /* Return function table of the pool. */ + return pool_funcs[pool->pool_id]; +} + +static inline void * +get_local( const CoreSurfacePool *pool ) +{ + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_ASSERT( pool->pool_id >= 0 ); + D_ASSERT( pool->pool_id < MAX_SURFACE_POOLS ); + + /* Return local data of the pool. */ + return pool_locals[pool->pool_id]; +} + +/**********************************************************************************************************************/ + +static DFBResult init_pool( CoreDFB *core, + CoreSurfacePool *pool, + const SurfacePoolFuncs *funcs ); + +/**********************************************************************************************************************/ + +static void insert_pool_local( CoreSurfacePool *pool ); +static void remove_pool_local( CoreSurfacePoolID pool_id ); + +/**********************************************************************************************************************/ + +static void remove_allocation( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation *allocation ); + +static DFBResult backup_allocation( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation *allocation ); + +/**********************************************************************************************************************/ + +DFBResult +dfb_surface_pool_initialize( CoreDFB *core, + const SurfacePoolFuncs *funcs, + CoreSurfacePool **ret_pool ) +{ + DFBResult ret; + CoreSurfacePool *pool; + FusionSHMPoolShared *shmpool; + + D_DEBUG_AT( Core_SurfacePool, "%s( %p )\n", __FUNCTION__, funcs ); + + D_ASSERT( core != NULL ); + D_ASSERT( funcs != NULL ); + D_ASSERT( ret_pool != NULL ); + + /* Check against pool limit. */ + if (pool_count == MAX_SURFACE_POOLS) { + D_ERROR( "Core/SurfacePool: Maximum number of pools (%d) reached!\n", MAX_SURFACE_POOLS ); + return DFB_LIMITEXCEEDED; + } + + D_ASSERT( pool_funcs[pool_count] == NULL ); + + shmpool = dfb_core_shmpool( core ); + + /* Allocate pool structure. */ + pool = SHCALLOC( shmpool, 1, sizeof(CoreSurfacePool) ); + if (!pool) + return D_OOSHM(); + + /* Assign a pool ID. */ + pool->pool_id = pool_count++; + + /* Remember shared memory pool. */ + pool->shmpool = shmpool; + + /* Set function table of the pool. */ + pool_funcs[pool->pool_id] = funcs; + + /* Add to global pool list. */ + pool_array[pool->pool_id] = pool; + + D_MAGIC_SET( pool, CoreSurfacePool ); + + ret = init_pool( core, pool, funcs ); + if (ret) { + pool_funcs[pool->pool_id] = NULL; + pool_array[pool->pool_id] = NULL; + pool_count--; + D_MAGIC_CLEAR( pool ); + SHFREE( shmpool, pool ); + return ret; + } + + /* Set default backup pool being the shared memory surface pool */ + if (!pool->backup && pool_count > 1) + pool->backup = pool_array[0]; + + /* Insert new pool into priority order */ + insert_pool_local( pool ); + + /* Return the new pool. */ + *ret_pool = pool; + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_join( CoreDFB *core, + CoreSurfacePool *pool, + const SurfacePoolFuncs *funcs ) +{ + DFBResult ret; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, funcs ); + + D_ASSERT( core != NULL ); + D_ASSERT( funcs != NULL ); + + D_ASSERT( pool->pool_id < MAX_SURFACE_POOLS ); + D_ASSERT( pool->pool_id == pool_count ); + D_ASSERT( pool_funcs[pool->pool_id] == NULL ); + + /* Enforce same order as initialization to be used during join. */ + if (pool->pool_id != pool_count) { + D_ERROR( "Core/SurfacePool: Wrong order of joining pools, got %d, should be %d!\n", + pool->pool_id, pool_count ); + return DFB_BUG; + } + + /* Allocate local pool data. */ + if (pool->pool_local_data_size && + !(pool_locals[pool->pool_id] = D_CALLOC( 1, pool->pool_local_data_size ))) + return D_OOM(); + + /* Set function table of the pool. */ + pool_funcs[pool->pool_id] = funcs; + + /* Add to global pool list. */ + pool_array[pool->pool_id] = pool; + + /* Adjust pool count. */ + if (pool_count < pool->pool_id + 1) + pool_count = pool->pool_id + 1; + + funcs = get_funcs( pool ); + + if (funcs->JoinPool) { + ret = funcs->JoinPool( core, pool, pool->data, get_local(pool), dfb_system_data() ); + if (ret) { + D_DERROR( ret, "Core/SurfacePool: Joining '%s' failed!\n", pool->desc.name ); + + if (pool_locals[pool->pool_id]) { + D_FREE( pool_locals[pool->pool_id] ); + pool_locals[pool->pool_id] = NULL; + } + + pool_count--; + + return ret; + } + } + + /* Insert new pool into priority order */ + insert_pool_local( pool ); + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_destroy( CoreSurfacePool *pool ) +{ + CoreSurfacePoolID pool_id; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + pool_id = pool->pool_id; + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, '%s' [%d] )\n", __FUNCTION__, pool, pool->desc.name, pool_id ); + + D_ASSERT( pool->pool_id >= 0 ); + D_ASSERT( pool_id < MAX_SURFACE_POOLS ); + D_ASSERT( pool_array[pool_id] == pool ); + + funcs = get_funcs( pool ); + + if (funcs->DestroyPool) + funcs->DestroyPool( pool, pool->data, get_local(pool) ); + + /* Free shared pool data. */ + if (pool->data) + SHFREE( pool->shmpool, pool->data ); + + /* Free local pool data and remove from lists */ + remove_pool_local( pool_id ); + + fusion_skirmish_destroy( &pool->lock ); + + fusion_vector_destroy( &pool->allocs ); + + D_MAGIC_CLEAR( pool ); + + SHFREE( pool->shmpool, pool ); + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_leave( CoreSurfacePool *pool ) +{ + CoreSurfacePoolID pool_id; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + pool_id = pool->pool_id; + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, '%s' [%d] )\n", __FUNCTION__, pool, pool->desc.name, pool_id ); + + D_ASSERT( pool->pool_id >= 0 ); + D_ASSERT( pool_id < MAX_SURFACE_POOLS ); + D_ASSERT( pool_array[pool_id] == pool ); + + funcs = get_funcs( pool ); + + if (funcs->LeavePool) + funcs->LeavePool( pool, pool->data, get_local(pool) ); + + /* Free local pool data and remove from lists */ + remove_pool_local( pool_id ); + + return DFB_OK; +} + +/**********************************************************************************************************************/ + +DFBResult +dfb_surface_pools_negotiate( CoreSurfaceBuffer *buffer, + CoreSurfaceAccessorID accessor, + CoreSurfaceAccessFlags access, + CoreSurfacePool **ret_pools, + unsigned int max_pools, + unsigned int *ret_num ) +{ + DFBResult ret; + int i; + unsigned int num = 0; + CoreSurface *surface; + CoreSurfaceTypeFlags type; + unsigned int free_count = 0; + CoreSurfacePool *free_pools[pool_count]; + unsigned int oom_count = 0; + CoreSurfacePool *oom_pools[pool_count]; + + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p [%s], 0x%02x, 0x%02x, max %d )\n", __FUNCTION__, + buffer, dfb_pixelformat_name( buffer->format ), accessor, access, max_pools ); + + D_ASSERT( ret_pools != NULL ); + D_ASSERT( max_pools > 0 ); + D_ASSERT( ret_num != NULL ); + + surface = buffer->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &surface->lock ); + + D_ASSERT( accessor >= CSAID_CPU ); + D_ASSUME( accessor < _CSAID_NUM ); + if (accessor >= CSAID_ANY) { + D_UNIMPLEMENTED(); + return DFB_UNIMPLEMENTED; + } + + if (accessor < 0 || accessor >= _CSAID_NUM) + return DFB_INVARG; + + type = surface->type & ~(CSTF_INTERNAL | CSTF_EXTERNAL); + + switch (buffer->policy) { + case CSP_SYSTEMONLY: + type |= CSTF_INTERNAL; + break; + + case CSP_VIDEOONLY: + type |= CSTF_EXTERNAL; + break; + + default: + break; + } + + D_DEBUG_AT( Core_SurfacePool, " -> 0x%02x 0x%03x required\n", access, type ); + + for (i=0; i<pool_count; i++) { + CoreSurfacePool *pool; + + D_ASSERT( pool_order[i] >= 0 ); + D_ASSERT( pool_order[i] < pool_count ); + + pool = pool_array[pool_order[i]]; + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + if (D_FLAGS_ARE_SET( pool->desc.access[accessor], access ) && + D_FLAGS_ARE_SET( pool->desc.types, type )) + { + const SurfacePoolFuncs *funcs; + + D_DEBUG_AT( Core_SurfacePool, " -> [%d] 0x%02x 0x%03x (%d) [%s]\n", pool->pool_id, + pool->desc.caps, pool->desc.types, pool->desc.priority, pool->desc.name ); + + funcs = get_funcs( pool ); + + ret = funcs->TestConfig ? funcs->TestConfig( pool, pool->data, get_local(pool), + buffer, &surface->config ) : DFB_OK; + switch (ret) { + case DFB_OK: + D_DEBUG_AT( Core_SurfacePool, " => OK\n" ); + free_pools[free_count++] = pool; + break; + + case DFB_NOVIDEOMEMORY: + D_DEBUG_AT( Core_SurfacePool, " => OUT OF MEMORY\n" ); + oom_pools[oom_count++] = pool; + break; + + default: + continue; + } + } + } + + D_DEBUG_AT( Core_SurfacePool, " => %d pools available\n", free_count ); + D_DEBUG_AT( Core_SurfacePool, " => %d pools out of memory\n", oom_count ); + + for (i=0; i<free_count && num<max_pools; i++) + ret_pools[num++] = free_pools[i]; + + for (i=0; i<oom_count && num<max_pools; i++) + ret_pools[num++] = oom_pools[i]; + + *ret_num = num; + + return free_count ? DFB_OK : oom_count ? DFB_NOVIDEOMEMORY : DFB_UNSUPPORTED; +} + +DFBResult +dfb_surface_pools_enumerate( CoreSurfacePoolCallback callback, + void *ctx ) +{ + int i; + + D_ASSERT( callback != NULL ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, %p )\n", __FUNCTION__, callback, ctx ); + + for (i=0; i<pool_count; i++) { + CoreSurfacePool *pool = pool_array[i]; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + if (callback( pool, ctx ) == DFENUM_CANCEL) + break; + } + + return DFB_OK; +} + +DFBResult +dfb_surface_pools_allocate( CoreSurfaceBuffer *buffer, + CoreSurfaceAccessorID accessor, + CoreSurfaceAccessFlags access, + CoreSurfaceAllocation **ret_allocation ) +{ + DFBResult ret; + int i; + CoreSurface *surface; + CoreSurfaceAllocation *allocation = NULL; + CoreSurfacePool *pools[pool_count]; + unsigned int num_pools; + + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + D_FLAGS_ASSERT( access, CSAF_ALL ); + D_ASSERT( ret_allocation != NULL ); + + surface = buffer->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &surface->lock ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, 0x%x )\n", __FUNCTION__, buffer, access ); + + D_DEBUG_AT( Core_SurfacePool, " -> %dx%d %s - %s%s%s%s%s%s%s%s\n", + surface->config.size.w, surface->config.size.h, + dfb_pixelformat_name( surface->config.format ), + (surface->type & CSTF_SHARED) ? "SHARED" : "PRIVATE", + (surface->type & CSTF_LAYER) ? " LAYER" : "", + (surface->type & CSTF_WINDOW) ? " WINDOW" : "", + (surface->type & CSTF_CURSOR) ? " CURSOR" : "", + (surface->type & CSTF_FONT) ? " FONT" : "", + (surface->type & CSTF_INTERNAL) ? " INTERNAL" : "", + (surface->type & CSTF_EXTERNAL) ? " EXTERNAL" : "", + (surface->type & CSTF_PREALLOCATED) ? " PREALLOCATED" : "" ); + + D_ASSERT( accessor >= CSAID_CPU ); + D_ASSUME( accessor < _CSAID_NUM ); + if (accessor >= CSAID_ANY) { + D_UNIMPLEMENTED(); + return DFB_UNIMPLEMENTED; + } + + if (accessor < 0 || accessor >= _CSAID_NUM) + return DFB_INVARG; + + /* Build a list of possible pools being free or out of memory */ + ret = dfb_surface_pools_negotiate( buffer, accessor, access, pools, pool_count, &num_pools ); + if (ret && ret != DFB_NOVIDEOMEMORY) { + D_DEBUG_AT( Core_SurfacePool, " -> NEGOTIATION FAILED! (%s)\n", DirectFBErrorString( ret ) ); + return ret; + } + + /* Try to do the allocation in one of the pools */ + for (i=0; i<num_pools; i++) { + CoreSurfacePool *pool = pools[i]; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + ret = dfb_surface_pool_allocate( pool, buffer, &allocation ); + + if (ret == DFB_OK) + break; + + /* When an error other than out of memory happens... */ + if (ret != DFB_NOVIDEOMEMORY) { + D_DEBUG_AT( Core_SurfacePool, " -> Allocation in '%s' failed!\n", pool->desc.name ); + + /* ...forget about the pool for now */ + pools[i] = NULL; + } + } + + /* Check if none of the pools could do the allocation */ + if (!allocation) { + /* Try to find a pool with "older" allocations to muck out */ + for (i=0; i<num_pools; i++) { + CoreSurfacePool *pool = pools[i]; + + /* Pools with non-oom errors were sorted out above */ + if (!pool) + continue; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + ret = dfb_surface_pool_displace( pool, buffer, &allocation ); + + if (ret == DFB_OK) + break; + } + } + + /* Still no luck? */ + if (!allocation) { + D_DEBUG_AT( Core_SurfacePool, " -> FAILED!\n" ); + return DFB_FAILURE; + } + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + D_DEBUG_AT( Core_SurfacePool, " -> %p\n", allocation ); + + *ret_allocation = allocation; + + return DFB_OK; +} + +/**********************************************************************************************************************/ + +DFBResult +dfb_surface_pool_allocate( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation **ret_allocation ) +{ + DFBResult ret; + int i; + CoreSurface *surface; + CoreSurfaceAllocation *allocation = NULL; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, buffer ); + + D_ASSERT( ret_allocation != NULL ); + + surface = buffer->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &surface->lock ); + + funcs = get_funcs( pool ); + + D_ASSERT( funcs->AllocateBuffer != NULL ); + + allocation = SHCALLOC( pool->shmpool, 1, sizeof(CoreSurfaceAllocation) ); + if (!allocation) + return D_OOSHM(); + + allocation->buffer = buffer; + allocation->surface = surface; + allocation->pool = pool; + allocation->access = pool->desc.access; + + if (pool->alloc_data_size) { + allocation->data = SHCALLOC( pool->shmpool, 1, pool->alloc_data_size ); + if (!allocation->data) { + ret = D_OOSHM(); + goto error; + } + } + + D_MAGIC_SET( allocation, CoreSurfaceAllocation ); + + if (fusion_skirmish_prevail( &pool->lock )) { + ret = DFB_FUSION; + goto error; + } + + if (dfb_config->warn.flags & DCWF_ALLOCATE_BUFFER && + dfb_config->warn.allocate_buffer.min_size.w <= surface->config.size.w && + dfb_config->warn.allocate_buffer.min_size.h <= surface->config.size.h) + D_WARN( "allocate-buffer %4dx%4d %6s, surface-caps 0x%08x", + surface->config.size.w, surface->config.size.h, dfb_pixelformat_name(buffer->format), + surface->config.caps ); + + ret = funcs->AllocateBuffer( pool, pool->data, get_local(pool), buffer, allocation, allocation->data ); + if (ret) { + D_DEBUG_AT( Core_SurfacePool, " -> %s\n", DirectFBErrorString( ret ) ); + D_MAGIC_CLEAR( allocation ); + fusion_skirmish_dismiss( &pool->lock ); + goto error; + } + + D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation ); + + if (allocation->flags & CSALF_ONEFORALL) { + for (i=0; i<surface->num_buffers; i++) { + buffer = surface->buffers[i]; + + D_ASSUME( fusion_vector_is_empty( &buffer->allocs ) ); + + D_DEBUG_AT( Core_SurfacePool, " -> %p (%d)\n", allocation, i ); + fusion_vector_add( &buffer->allocs, allocation ); + fusion_vector_add( &pool->allocs, allocation ); + } + } + else { + D_DEBUG_AT( Core_SurfacePool, " -> %p\n", allocation ); + fusion_vector_add( &buffer->allocs, allocation ); + fusion_vector_add( &pool->allocs, allocation ); + } + + direct_serial_init( &allocation->serial ); + + fusion_skirmish_dismiss( &pool->lock ); + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + *ret_allocation = allocation; + + return DFB_OK; + +error: + if (allocation->data) + SHFREE( pool->shmpool, allocation->data ); + + SHFREE( pool->shmpool, allocation ); + + return ret; +} + +DFBResult +dfb_surface_pool_deallocate( CoreSurfacePool *pool, + CoreSurfaceAllocation *allocation ) +{ + DFBResult ret; + int i; + const SurfacePoolFuncs *funcs; + CoreSurfaceBuffer *buffer; + CoreSurface *surface; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, allocation ); + + D_ASSERT( pool == allocation->pool ); + + buffer = allocation->buffer; + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + + surface = buffer->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &surface->lock ); + + funcs = get_funcs( pool ); + + D_ASSERT( funcs->DeallocateBuffer != NULL ); + + if (fusion_skirmish_prevail( &pool->lock )) + return DFB_FUSION; + + ret = funcs->DeallocateBuffer( pool, pool->data, get_local(pool), allocation->buffer, allocation, allocation->data ); + if (ret) { + D_DERROR( ret, "Core/SurfacePool: Could not deallocate buffer!\n" ); + fusion_skirmish_dismiss( &pool->lock ); + return ret; + } + + if (allocation->flags & CSALF_ONEFORALL) { + for (i=0; i<surface->num_buffers; i++) + remove_allocation( pool, surface->buffers[i], allocation ); + } + else + remove_allocation( pool, buffer, allocation ); + + fusion_skirmish_dismiss( &pool->lock ); + + if (allocation->data) + SHFREE( pool->shmpool, allocation->data ); + + direct_serial_deinit( &allocation->serial ); + + D_MAGIC_CLEAR( allocation ); + + SHFREE( pool->shmpool, allocation ); + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_displace( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation **ret_allocation ) +{ + DFBResult ret, ret_lock = DFB_OK; + int i, retries = 3; + CoreSurface *surface; + CoreSurfaceAllocation *allocation; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, buffer ); + + D_ASSERT( ret_allocation != NULL ); + + surface = buffer->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &surface->lock ); + + funcs = get_funcs( pool ); + + if (fusion_skirmish_prevail( &pool->lock )) + return DFB_FUSION; + + /* Check for integrated method to muck out "older" allocations for a new one */ + if (funcs->MuckOut) { + ret = funcs->MuckOut( pool, pool->data, get_local(pool), buffer ); + if (ret) { + fusion_skirmish_dismiss( &pool->lock ); + return ret; + } + } + else { + /* Or take the generic approach via allocation list */ + D_UNIMPLEMENTED(); + } + + /* FIXME: Solve potential dead lock, until then do a few retries... */ +fixme_retry: + fusion_vector_foreach (allocation, i, pool->allocs) { + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + if (allocation->flags & CSALF_MUCKOUT) { + CoreSurface *alloc_surface; + CoreSurfaceBuffer *alloc_buffer; + + alloc_buffer = allocation->buffer; + D_MAGIC_ASSERT( alloc_buffer, CoreSurfaceBuffer ); + + alloc_surface = alloc_buffer->surface; + D_MAGIC_ASSERT( alloc_surface, CoreSurface ); + + D_DEBUG_AT( Core_SurfacePool, " <= %p %5dk, %lu\n", + allocation, allocation->size / 1024, allocation->offset ); + + /* FIXME: Solve potential dead lock, until then only try to lock... */ + ret = dfb_surface_trylock( alloc_surface ); + if (ret) { + D_WARN( "could not lock surface (%s)", DirectFBErrorString(ret) ); + ret_lock = ret; + continue; + } + + /* Ensure mucked out allocation is backed up in another pool */ + ret = backup_allocation( pool, buffer, allocation ); + if (ret) { + D_WARN( "could not backup allocation (%s)", DirectFBErrorString(ret) ); + dfb_surface_unlock( alloc_surface ); + goto error_cleanup; + } + + /* Deallocate mucked out allocation */ + dfb_surface_pool_deallocate( pool, allocation ); + i--; + + dfb_surface_unlock( alloc_surface ); + } + } + + /* FIXME: Solve potential dead lock, until then do a few retries... */ + if (ret_lock) { + if (retries--) + goto fixme_retry; + + ret = DFB_LOCKED; + + goto error_cleanup; + } + else + ret = dfb_surface_pool_allocate( pool, buffer, ret_allocation ); + + fusion_skirmish_dismiss( &pool->lock ); + + return ret; + + +error_cleanup: + fusion_vector_foreach (allocation, i, pool->allocs) { + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + if (allocation->flags & CSALF_MUCKOUT) + allocation->flags &= ~CSALF_MUCKOUT; + } + + fusion_skirmish_dismiss( &pool->lock ); + + return ret; +} + +DFBResult +dfb_surface_pool_lock( CoreSurfacePool *pool, + CoreSurfaceAllocation *allocation, + CoreSurfaceBufferLock *lock ) +{ + DFBResult ret; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfPoolLock, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, allocation ); + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + CORE_SURFACE_BUFFER_LOCK_ASSERT( lock ); + D_ASSERT( lock->buffer == NULL ); + + D_ASSERT( pool == allocation->pool ); + + funcs = get_funcs( pool ); + + D_ASSERT( funcs->Lock != NULL ); + + lock->allocation = allocation; + lock->buffer = allocation->buffer; + + ret = funcs->Lock( pool, pool->data, get_local(pool), allocation, allocation->data, lock ); + if (ret) { + D_DERROR( ret, "Core/SurfacePool: Could not lock allocation!\n" ); + dfb_surface_buffer_lock_reset( lock ); + return ret; + } + + CORE_SURFACE_BUFFER_LOCK_ASSERT( lock ); + D_ASSERT( lock->buffer != NULL ); + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_unlock( CoreSurfacePool *pool, + CoreSurfaceAllocation *allocation, + CoreSurfaceBufferLock *lock ) +{ + DFBResult ret; + const SurfacePoolFuncs *funcs; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfPoolLock, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, allocation ); + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + CORE_SURFACE_BUFFER_LOCK_ASSERT( lock ); + D_ASSERT( lock->buffer != NULL ); + + D_ASSERT( pool == allocation->pool ); + + funcs = get_funcs( pool ); + + D_ASSERT( funcs->Unlock != NULL ); + + ret = funcs->Unlock( pool, pool->data, get_local(pool), allocation, allocation->data, lock ); + if (ret) { + D_DERROR( ret, "Core/SurfacePool: Could not unlock allocation!\n" ); + return ret; + } + + CORE_SURFACE_BUFFER_LOCK_ASSERT( lock ); + D_ASSERT( lock->buffer != NULL ); + + dfb_surface_buffer_lock_reset( lock ); + + return DFB_OK; +} + +DFBResult +dfb_surface_pool_read( CoreSurfacePool *pool, + CoreSurfaceAllocation *allocation, + void *data, + int pitch, + const DFBRectangle *rect ) +{ + DFBResult ret; + const SurfacePoolFuncs *funcs; + CoreSurface *surface; + DFBRectangle area; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfPoolLock, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, allocation ); + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + D_ASSERT( data != NULL ); + D_ASSERT( pitch >= 0 ); + DFB_RECTANGLE_ASSERT_IF( rect ); + + D_ASSERT( pool == allocation->pool ); + + funcs = get_funcs( pool ); + D_ASSERT( funcs != NULL ); + + if (!funcs->Read) + return DFB_UNSUPPORTED; + + surface = allocation->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + + area.x = 0; + area.y = 0; + area.w = surface->config.size.w; + area.h = surface->config.size.h; + + if (rect && !dfb_rectangle_intersect( &area, rect )) + return DFB_INVAREA; + + ret = funcs->Read( pool, pool->data, get_local(pool), allocation, allocation->data, data, pitch, &area ); + if (ret) + D_DERROR( ret, "Core/SurfacePool: Could not read from allocation!\n" ); + + return ret; +} + +DFBResult +dfb_surface_pool_write( CoreSurfacePool *pool, + CoreSurfaceAllocation *allocation, + const void *data, + int pitch, + const DFBRectangle *rect ) +{ + DFBResult ret; + const SurfacePoolFuncs *funcs; + CoreSurface *surface; + DFBRectangle area; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfPoolLock, "%s( %p [%d], %p )\n", __FUNCTION__, pool, pool->pool_id, allocation ); + + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + D_ASSERT( data != NULL ); + D_ASSERT( pitch >= 0 ); + DFB_RECTANGLE_ASSERT_IF( rect ); + + D_ASSERT( pool == allocation->pool ); + + funcs = get_funcs( pool ); + D_ASSERT( funcs != NULL ); + + if (!funcs->Write) + return DFB_UNSUPPORTED; + + surface = allocation->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + + area.x = 0; + area.y = 0; + area.w = surface->config.size.w; + area.h = surface->config.size.h; + + if (rect && !dfb_rectangle_intersect( &area, rect )) + return DFB_INVAREA; + + ret = funcs->Write( pool, pool->data, get_local(pool), allocation, allocation->data, data, pitch, &area ); + if (ret) + D_DERROR( ret, "Core/SurfacePool: Could not write to allocation!\n" ); + + return ret; +} + +DFBResult +dfb_surface_pool_enumerate ( CoreSurfacePool *pool, + CoreSurfaceAllocCallback callback, + void *ctx ) +{ + int i; + CoreSurfaceAllocation *allocation; + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, %p, %p )\n", __FUNCTION__, pool, callback, ctx ); + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_ASSERT( callback != NULL ); + + fusion_vector_foreach (allocation, i, pool->allocs) { + if (callback( allocation, ctx ) == DFENUM_CANCEL) + break; + } + + return DFB_OK; +} + +/**********************************************************************************************************************/ + +static DFBResult +init_pool( CoreDFB *core, + CoreSurfacePool *pool, + const SurfacePoolFuncs *funcs ) +{ + DFBResult ret; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_ASSERT( funcs != NULL ); + D_ASSERT( funcs->InitPool != NULL ); + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, %p )\n", __FUNCTION__, pool, funcs ); + + if (funcs->PoolDataSize) + pool->pool_data_size = funcs->PoolDataSize(); + + if (funcs->PoolLocalDataSize) + pool->pool_local_data_size = funcs->PoolLocalDataSize(); + + if (funcs->AllocationDataSize) + pool->alloc_data_size = funcs->AllocationDataSize(); + + /* Allocate shared pool data. */ + if (pool->pool_data_size) { + pool->data = SHCALLOC( pool->shmpool, 1, pool->pool_data_size ); + if (!pool->data) + return D_OOSHM(); + } + + /* Allocate local pool data. */ + if (pool->pool_local_data_size && + !(pool_locals[pool->pool_id] = D_CALLOC( 1, pool->pool_local_data_size ))) + { + SHFREE( pool->shmpool, pool->data ); + return D_OOM(); + } + + fusion_vector_init( &pool->allocs, 4, pool->shmpool ); + + ret = funcs->InitPool( core, pool, pool->data, get_local(pool), dfb_system_data(), &pool->desc ); + if (ret) { + D_DERROR( ret, "Core/SurfacePool: Initializing '%s' failed!\n", pool->desc.name ); + + if (pool_locals[pool->pool_id]) { + D_FREE( pool_locals[pool->pool_id] ); + pool_locals[pool->pool_id] = NULL; + } + if (pool->data) { + SHFREE( pool->shmpool, pool->data ); + pool->data = NULL; + } + return ret; + } + + fusion_skirmish_init( &pool->lock, pool->desc.name, dfb_core_world(core) ); + + return DFB_OK; +} + +static void +insert_pool_local( CoreSurfacePool *pool ) +{ + int i, n; + + for (i=0; i<pool_count-1; i++) { + D_ASSERT( pool_order[i] >= 0 ); + D_ASSERT( pool_order[i] < pool_count-1 ); + + D_MAGIC_ASSERT( pool_array[pool_order[i]], CoreSurfacePool ); + + if (pool_array[pool_order[i]]->desc.priority < pool->desc.priority) + break; + } + + for (n=pool_count-1; n>i; n--) { + D_ASSERT( pool_order[n-1] >= 0 ); + D_ASSERT( pool_order[n-1] < pool_count-1 ); + + D_MAGIC_ASSERT( pool_array[pool_order[n-1]], CoreSurfacePool ); + + pool_order[n] = pool_order[n-1]; + } + + pool_order[n] = pool_count - 1; + +#if D_DEBUG_ENABLED + for (i=0; i<pool_count; i++) { + D_DEBUG_AT( Core_SurfacePool, " %c> [%d] %p - '%s' [%d] (%d), %p\n", + (i == n) ? '=' : '-', i, pool_array[pool_order[i]], pool_array[pool_order[i]]->desc.name, + pool_array[pool_order[i]]->pool_id, pool_array[pool_order[i]]->desc.priority, + pool_funcs[pool_order[i]] ); + D_ASSERT( pool_order[i] == pool_array[pool_order[i]]->pool_id ); + } +#endif +} + +static void +remove_pool_local( CoreSurfacePoolID pool_id ) +{ + int i; + + /* Free local pool data. */ + if (pool_locals[pool_id]) { + D_FREE( pool_locals[pool_id] ); + pool_locals[pool_id] = NULL; + } + + /* Erase entries of the pool. */ + pool_array[pool_id] = NULL; + pool_funcs[pool_id] = NULL; + + while (pool_count > 0 && !pool_array[pool_count-1]) { + pool_count--; + + for (i=0; i<pool_count; i++) { + if (pool_order[i] == pool_count) { + direct_memmove( &pool_order[i], &pool_order[i+1], sizeof(pool_order[0]) * (pool_count - i) ); + break; + } + } + } +} + +/**********************************************************************************************************************/ + +static void +remove_allocation( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation *allocation ) +{ + int index_buffer; + int index_pool; + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + D_MAGIC_ASSERT( buffer->surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &buffer->surface->lock ); + FUSION_SKIRMISH_ASSERT( &pool->lock ); + D_ASSERT( pool == allocation->pool ); + + /* Lookup indices within vectors */ + index_buffer = fusion_vector_index_of( &buffer->allocs, allocation ); + index_pool = fusion_vector_index_of( &pool->allocs, allocation ); + + D_ASSERT( index_buffer >= 0 ); + D_ASSERT( index_pool >= 0 ); + + /* Remove allocation from buffer and pool */ + fusion_vector_remove( &buffer->allocs, index_buffer ); + fusion_vector_remove( &pool->allocs, index_pool ); + + /* Update 'written' allocation pointer of buffer */ + if (buffer->written == allocation) { + /* Reset pointer first */ + buffer->written = NULL; + + /* Iterate through remaining allocations */ + fusion_vector_foreach (allocation, index_buffer, buffer->allocs) { + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + + /* Check if allocation is up to date and set it as 'written' allocation */ + if (direct_serial_check( &allocation->serial, &buffer->serial )) { + buffer->written = allocation; + break; + } + } + } + + /* Reset 'read' allocation pointer of buffer */ + if (buffer->read == allocation) + buffer->read = NULL; +} + +static DFBResult +backup_allocation( CoreSurfacePool *pool, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation *allocation ) +{ + DFBResult ret = DFB_OK; + int i; + CoreSurfaceAllocation *backup = NULL; + + D_DEBUG_AT( Core_SurfacePool, "%s( %p, %p )\n", __FUNCTION__, pool, allocation ); + + D_MAGIC_ASSERT( pool, CoreSurfacePool ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + CORE_SURFACE_ALLOCATION_ASSERT( allocation ); + D_MAGIC_ASSERT( buffer->surface, CoreSurface ); + FUSION_SKIRMISH_ASSERT( &buffer->surface->lock ); + FUSION_SKIRMISH_ASSERT( &pool->lock ); + D_ASSERT( pool == allocation->pool ); + + /* Check if allocation is the only up to date (requiring a backup) */ + if (direct_serial_check( &allocation->serial, &buffer->serial )) { + CoreSurfacePool *backup_pool = pool->backup; + + /* First check if any of the existing allocations is up to date */ + fusion_vector_foreach (backup, i, buffer->allocs) { + D_MAGIC_ASSERT( backup, CoreSurfaceAllocation ); + D_MAGIC_ASSERT( backup->pool, CoreSurfacePool ); + + if (backup->pool != pool && direct_serial_check( &backup->serial, &buffer->serial )) { + D_DEBUG_AT( Core_SurfacePool, " -> up to date in '%s'\n", backup->pool->desc.name ); + return DFB_OK; + } + } + + /* Try to update one of the existing allocations */ + fusion_vector_foreach (backup, i, buffer->allocs) { + D_MAGIC_ASSERT( backup, CoreSurfaceAllocation ); + D_MAGIC_ASSERT( backup->pool, CoreSurfacePool ); + + if (backup->pool != pool && dfb_surface_allocation_update( backup, CSAF_NONE ) == DFB_OK) { + D_DEBUG_AT( Core_SurfacePool, " -> updated in '%s'\n", backup->pool->desc.name ); + return DFB_OK; + } + } + + /* Try the designated backup pool and theirs if failing */ + while (backup_pool) { + D_MAGIC_ASSERT( backup_pool, CoreSurfacePool ); + + D_DEBUG_AT( Core_SurfacePool, " -> allocating in '%s'\n", backup_pool->desc.name ); + + /* Allocate in backup pool */ + ret = dfb_surface_pool_allocate( backup_pool, buffer, &backup ); + if (ret == DFB_OK) { + /* Update new allocation */ + ret = dfb_surface_allocation_update( backup, CSAF_NONE ); + if (ret) { + D_DEBUG_AT( Core_SurfacePool, " -> update failed! (%s)\n", DirectFBErrorString(ret) ); + dfb_surface_pool_deallocate( backup_pool, backup ); + backup = NULL; + } + else + return DFB_OK; + } + else + D_DEBUG_AT( Core_SurfacePool, " -> allocation failed! (%s)\n", DirectFBErrorString(ret) ); + + backup_pool = backup_pool->backup; + } + } + else + D_DEBUG_AT( Core_SurfacePool, " -> not up to date anyhow\n" ); + + return ret; +} + |