From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/systems/devmem/surfacemanager.c | 575 ++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100755 Source/DirectFB/systems/devmem/surfacemanager.c (limited to 'Source/DirectFB/systems/devmem/surfacemanager.c') diff --git a/Source/DirectFB/systems/devmem/surfacemanager.c b/Source/DirectFB/systems/devmem/surfacemanager.c new file mode 100755 index 0000000..c3330cf --- /dev/null +++ b/Source/DirectFB/systems/devmem/surfacemanager.c @@ -0,0 +1,575 @@ +/* + (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 , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + 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 + +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "surfacemanager.h" + +D_DEBUG_DOMAIN( SurfMan, "SurfaceManager", "DirectFB Surface Manager" ); + + +static Chunk *split_chunk ( SurfaceManager *manager, + Chunk *chunk, + int length ); + +static Chunk *free_chunk ( SurfaceManager *manager, + Chunk *chunk ); + +static Chunk *occupy_chunk( SurfaceManager *manager, + Chunk *chunk, + CoreSurfaceAllocation *allocation, + int length, + int pitch ); + + +DFBResult +dfb_surfacemanager_create( CoreDFB *core, + unsigned int length, + SurfaceManager **ret_manager ) +{ + FusionSHMPoolShared *pool; + SurfaceManager *manager; + Chunk *chunk; + + D_DEBUG_AT( SurfMan, "%s( %p, %d )\n", __FUNCTION__, core, length ); + + D_ASSERT( core != NULL ); + D_ASSERT( ret_manager != NULL ); + + pool = dfb_core_shmpool( core ); + + manager = SHCALLOC( pool, 1, sizeof(SurfaceManager) ); + if (!manager) + return D_OOSHM(); + + chunk = SHCALLOC( pool, 1, sizeof(Chunk) ); + if (!chunk) { + D_OOSHM(); + SHFREE( pool, manager ); + return DFB_NOSHAREDMEMORY; + } + + manager->shmpool = pool; + manager->chunks = chunk; + manager->offset = 0; + manager->length = length; + manager->avail = manager->length - manager->offset; + + D_MAGIC_SET( manager, SurfaceManager ); + + chunk->offset = manager->offset; + chunk->length = manager->avail; + + D_MAGIC_SET( chunk, Chunk ); + + D_DEBUG_AT( SurfMan, " -> %p\n", manager ); + + *ret_manager = manager; + + return DFB_OK; +} + +void +dfb_surfacemanager_destroy( SurfaceManager *manager ) +{ + Chunk *chunk; + void *next; + + D_DEBUG_AT( SurfMan, "%s( %p )\n", __FUNCTION__, manager ); + + D_MAGIC_ASSERT( manager, SurfaceManager ); + + /* Deallocate all video chunks. */ + chunk = manager->chunks; + while (chunk) { + next = chunk->next; + + D_MAGIC_CLEAR( chunk ); + + SHFREE( manager->shmpool, chunk ); + + chunk = next; + } + + D_MAGIC_CLEAR( manager ); + + /* Deallocate manager struct. */ + SHFREE( manager->shmpool, manager ); +} + +/** public functions NOT locking the surfacemanger theirself, + to be called between lock/unlock of surfacemanager **/ + +DFBResult dfb_surfacemanager_allocate( CoreDFB *core, + SurfaceManager *manager, + CoreSurfaceBuffer *buffer, + CoreSurfaceAllocation *allocation, + Chunk **ret_chunk ) +{ + int pitch; + int length; + Chunk *c; + CoreGraphicsDevice *device; + + Chunk *best_free = NULL; + + D_MAGIC_ASSERT( manager, SurfaceManager ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + D_MAGIC_ASSERT( buffer->surface, CoreSurface ); + + if (ret_chunk) + D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation ); + else + D_ASSUME( allocation == NULL ); + + D_DEBUG_AT( SurfMan, "%s( %p ) <- %dx%d %s\n", __FUNCTION__, buffer, + buffer->surface->config.size.w, buffer->surface->config.size.h, + dfb_pixelformat_name( buffer->surface->config.format ) ); + + if (manager->suspended) + return DFB_SUSPENDED; + + /* FIXME: Only one global device at the moment. */ + device = dfb_core_get_part( core, DFCP_GRAPHICS ); + D_ASSERT( device != NULL ); + + dfb_gfxcard_calc_buffer_size( device, buffer, &pitch, &length ); + + D_DEBUG_AT( SurfMan, " -> pitch %d, length %d\n", pitch, length ); + + if (manager->avail < length) + return DFB_TEMPUNAVAIL; + + /* examine chunks */ + c = manager->chunks; + D_MAGIC_ASSERT( c, Chunk ); + + /* FIXME_SC_2 Workaround creation happening before graphics driver initialization. */ + if (!c->next) { + int length = dfb_gfxcard_memory_length(); + + if (c->length != length - manager->offset) { + D_WARN( "workaround" ); + + manager->length = length; + manager->avail = length - manager->offset; + + c->length = length - manager->offset; + } + } + + while (c) { + D_MAGIC_ASSERT( c, Chunk ); + + if (!c->buffer && c->length >= length) { + /* NULL means check only. */ + if (!ret_chunk) + return DFB_OK; + + /* found a nice place to chill */ + if (!best_free || best_free->length > c->length) + /* first found or better one? */ + best_free = c; + + if (c->length == length) + break; + } + + c = c->next; + } + + /* if we found a place */ + if (best_free) { + D_DEBUG_AT( SurfMan, " -> found free (%d)\n", best_free->length ); + + /* NULL means check only. */ + if (ret_chunk) + *ret_chunk = occupy_chunk( manager, best_free, allocation, length, pitch ); + + return DFB_OK; + } + + D_DEBUG_AT( SurfMan, " -> failed (%d/%d avail)\n", manager->avail, manager->length ); + + /* no luck */ + return DFB_NOVIDEOMEMORY; +} + +DFBResult dfb_surfacemanager_displace( CoreDFB *core, + SurfaceManager *manager, + CoreSurfaceBuffer *buffer ) +{ + int length; + Chunk *multi_start = NULL; + int multi_size = 0; + int multi_tsize = 0; + int multi_count = 0; + Chunk *bestm_start = NULL; + int bestm_count = 0; + int bestm_size = 0; + int min_toleration; + Chunk *chunk; + CoreGraphicsDevice *device; + CoreSurfaceAllocation *smallest = NULL; + + D_MAGIC_ASSERT( manager, SurfaceManager ); + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + D_MAGIC_ASSERT( buffer->surface, CoreSurface ); + + D_DEBUG_AT( SurfMan, "%s( %p ) <- %dx%d %s\n", __FUNCTION__, buffer, + buffer->surface->config.size.w, buffer->surface->config.size.h, + dfb_pixelformat_name( buffer->surface->config.format ) ); + + /* FIXME: Only one global device at the moment. */ + device = dfb_core_get_part( core, DFCP_GRAPHICS ); + D_ASSERT( device != NULL ); + + dfb_gfxcard_calc_buffer_size( dfb_core_get_part( core, DFCP_GRAPHICS ), buffer, NULL, &length ); + + min_toleration = manager->min_toleration/8 + 2; + + D_DEBUG_AT( SurfMan, " -> %7d required, min toleration %d\n", length, min_toleration ); + + chunk = manager->chunks; + while (chunk) { + CoreSurfaceAllocation *allocation; + + D_MAGIC_ASSERT( chunk, Chunk ); + + allocation = chunk->allocation; + if (allocation) { + CoreSurfaceBuffer *other; + int size; + + D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation ); + D_ASSERT( chunk->buffer == allocation->buffer ); + D_ASSERT( chunk->length >= allocation->size ); + + other = allocation->buffer; + D_MAGIC_ASSERT( other, CoreSurfaceBuffer ); + + if (other->locked) { + D_DEBUG_AT( SurfMan, " ++ %7d locked %dx\n", allocation->size, other->locked ); + goto next_reset; + } + + if (other->policy > buffer->policy) { + D_DEBUG_AT( SurfMan, " ++ %7d policy %d > %d\n", allocation->size, other->policy, buffer->policy ); + goto next_reset; + } + + if (other->policy == CSP_VIDEOONLY) { + D_DEBUG_AT( SurfMan, " ++ %7d policy videoonly\n", allocation->size ); + goto next_reset; + } + + chunk->tolerations++; + if (chunk->tolerations > 0xff) + chunk->tolerations = 0xff; + + if (other->policy == buffer->policy && chunk->tolerations < min_toleration) { + D_DEBUG_AT( SurfMan, " ++ %7d tolerations %d/%d\n", + allocation->size, chunk->tolerations, min_toleration ); + goto next_reset; + } + + size = allocation->size; + + if (chunk->prev && !chunk->prev->allocation) + size += chunk->prev->length; + + if (chunk->next && !chunk->next->allocation) + size += chunk->next->length; + + if (size >= length) { + if (!smallest || smallest->size > allocation->size) { + D_DEBUG_AT( SurfMan, " => %7d [%d] < %d, tolerations %d\n", + allocation->size, size, smallest ? smallest->size : 0, chunk->tolerations ); + + smallest = allocation; + } + else + D_DEBUG_AT( SurfMan, " -> %7d [%d] > %d\n", allocation->size, size, smallest->size ); + } + else + D_DEBUG_AT( SurfMan, " -> %7d [%d]\n", allocation->size, size ); + } + else + D_DEBUG_AT( SurfMan, " - %7d free\n", chunk->length ); + + + if (!smallest) { + if (!multi_start) { + multi_start = chunk; + multi_tsize = chunk->length; + multi_size = chunk->allocation ? chunk->length : 0; + multi_count = chunk->allocation ? 1 : 0; + } + else { + multi_tsize += chunk->length; + multi_size += chunk->allocation ? chunk->length : 0; + multi_count += chunk->allocation ? 1 : 0; + + while (multi_tsize >= length && multi_count > 1) { + if (!bestm_start || bestm_size > multi_size * multi_count / bestm_count) { + D_DEBUG_AT( SurfMan, " =====> %7d, %7d %2d used [%7d %2d]\n", + multi_tsize, multi_size, multi_count, bestm_size, bestm_count ); + + bestm_size = multi_size; + bestm_start = multi_start; + bestm_count = multi_count; + } + else + D_DEBUG_AT( SurfMan, " -----> %7d, %7d %2d used\n", + multi_tsize, multi_size, multi_count ); + + if (multi_count <= 2) + break; + + if (!multi_start->allocation) { + multi_tsize -= multi_start->length; + multi_start = multi_start->next; + } + + D_ASSUME( multi_start->allocation != NULL ); + + multi_tsize -= multi_start->length; + multi_size -= multi_start->allocation ? multi_start->length : 0; + multi_count -= multi_start->allocation ? 1 : 0; + multi_start = multi_start->next; + } + } + } + + chunk = chunk->next; + + continue; + + +next_reset: + multi_start = NULL; + + chunk = chunk->next; + } + + if (smallest) { + D_MAGIC_ASSERT( smallest, CoreSurfaceAllocation ); + D_MAGIC_ASSERT( smallest->buffer, CoreSurfaceBuffer ); + + smallest->flags |= CSALF_MUCKOUT; + + D_DEBUG_AT( SurfMan, " -> offset %lu, size %d\n", smallest->offset, smallest->size ); + + return DFB_OK; + } + + if (bestm_start) { + chunk = bestm_start; + + while (bestm_count) { + CoreSurfaceAllocation *allocation = chunk->allocation; + + if (allocation) { + D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation ); + D_MAGIC_ASSERT( allocation->buffer, CoreSurfaceBuffer ); + + allocation->flags |= CSALF_MUCKOUT; + + bestm_count--; + } + + D_DEBUG_AT( SurfMan, " ---> offset %d, length %d\n", chunk->offset, chunk->length ); + + chunk = chunk->next; + } + + return DFB_OK; + } + + return DFB_NOVIDEOMEMORY; +} + +DFBResult dfb_surfacemanager_deallocate( SurfaceManager *manager, + Chunk *chunk ) +{ + CoreSurfaceBuffer *buffer; + + D_MAGIC_ASSERT( manager, SurfaceManager ); + D_MAGIC_ASSERT( chunk, Chunk ); + + buffer = chunk->buffer; + D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); + D_MAGIC_ASSERT( buffer->surface, CoreSurface ); + + D_DEBUG_AT( SurfMan, "%s( %p ) <- %dx%d %s\n", __FUNCTION__, buffer, + buffer->surface->config.size.w, buffer->surface->config.size.h, + dfb_pixelformat_name( buffer->surface->config.format ) ); + + free_chunk( manager, chunk ); + + return DFB_OK; +} + +/** internal functions NOT locking the surfacemanager **/ + +static Chunk * +split_chunk( SurfaceManager *manager, Chunk *c, int length ) +{ + Chunk *newchunk; + + D_MAGIC_ASSERT( c, Chunk ); + + if (c->length == length) /* does not need be splitted */ + return c; + + newchunk = (Chunk*) SHCALLOC( manager->shmpool, 1, sizeof(Chunk) ); + if (!newchunk) { + D_OOSHM(); + return NULL; + } + + /* calculate offsets and lengths of resulting chunks */ + newchunk->offset = c->offset + c->length - length; + newchunk->length = length; + c->length -= newchunk->length; + + /* insert newchunk after chunk c */ + newchunk->prev = c; + newchunk->next = c->next; + if (c->next) + c->next->prev = newchunk; + c->next = newchunk; + + D_MAGIC_SET( newchunk, Chunk ); + + return newchunk; +} + +static Chunk * +free_chunk( SurfaceManager *manager, Chunk *chunk ) +{ + D_MAGIC_ASSERT( manager, SurfaceManager ); + D_MAGIC_ASSERT( chunk, Chunk ); + + if (!chunk->buffer) { + D_BUG( "freeing free chunk" ); + return chunk; + } + else { + D_DEBUG_AT( SurfMan, "Deallocating %d bytes at offset %d.\n", chunk->length, chunk->offset ); + } + + if (chunk->buffer->policy == CSP_VIDEOONLY) + manager->avail += chunk->length; + + chunk->allocation = NULL; + chunk->buffer = NULL; + + manager->min_toleration--; + + if (chunk->prev && !chunk->prev->buffer) { + Chunk *prev = chunk->prev; + + //D_DEBUG_AT( SurfMan, " -> merging with previous chunk at %d\n", prev->offset ); + + prev->length += chunk->length; + + prev->next = chunk->next; + if (prev->next) + prev->next->prev = prev; + + //D_DEBUG_AT( SurfMan, " -> freeing %p (prev %p, next %p)\n", chunk, chunk->prev, chunk->next); + + D_MAGIC_CLEAR( chunk ); + + SHFREE( manager->shmpool, chunk ); + chunk = prev; + } + + if (chunk->next && !chunk->next->buffer) { + Chunk *next = chunk->next; + + //D_DEBUG_AT( SurfMan, " -> merging with next chunk at %d\n", next->offset ); + + chunk->length += next->length; + + chunk->next = next->next; + if (chunk->next) + chunk->next->prev = chunk; + + D_MAGIC_CLEAR( next ); + + SHFREE( manager->shmpool, next ); + } + + return chunk; +} + +static Chunk * +occupy_chunk( SurfaceManager *manager, Chunk *chunk, CoreSurfaceAllocation *allocation, int length, int pitch ) +{ + D_MAGIC_ASSERT( manager, SurfaceManager ); + D_MAGIC_ASSERT( chunk, Chunk ); + D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation ); + D_MAGIC_ASSERT( allocation->buffer, CoreSurfaceBuffer ); + + if (allocation->buffer->policy == CSP_VIDEOONLY) + manager->avail -= length; + + chunk = split_chunk( manager, chunk, length ); + if (!chunk) + return NULL; + + D_DEBUG_AT( SurfMan, "Allocating %d bytes at offset %d.\n", chunk->length, chunk->offset ); + + chunk->allocation = allocation; + chunk->buffer = allocation->buffer; + chunk->pitch = pitch; + + manager->min_toleration++; + + return chunk; +} + -- cgit