Files
directfb/src/core/surface_allocation.cpp
Denis Oliver Kropp e000416a55 C++: fix some includes
2014-01-20 19:43:51 +01:00

749 lines
24 KiB
C++

/*
(c) Copyright 2012-2013 DirectFB integrated media GmbH
(c) Copyright 2001-2013 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 Shimokawa <andi@directfb.org>,
Marek Pikarski <mass@directfb.org>,
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.
*/
//#define DIRECT_ENABLE_DEBUG
#include <config.h>
#include <direct/Types++.h>
#include <directfb.h> // include here to prevent it being included indirectly causing nested extern "C"
extern "C" {
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef USE_ZLIB
#include <zlib.h>
#endif
#include <directfb_util.h>
#include <direct/debug.h>
#include <direct/memcpy.h>
#include <fusion/shmalloc.h>
#include <core/CoreSurface.h>
#include <core/CoreSurfaceAllocation.h>
#include <core/gfxcard.h>
#include <core/palette.h>
#include <core/surface.h>
#include <core/surface_buffer.h>
#include <core/surface_pool.h>
#include <core/surface_pool_bridge.h>
#include <misc/conf.h>
#include <gfx/convert.h>
}
#include <direct/Lists.h>
#include <core/Debug.h>
#include <core/SurfaceTask.h>
D_DEBUG_DOMAIN( Core_SurfAllocation, "Core/SurfAllocation", "DirectFB Core Surface Allocation" );
/**********************************************************************************************************************/
static void
surface_allocation_destructor( FusionObject *object, bool zombie, void *ctx )
{
CoreSurfaceAllocation *allocation = (CoreSurfaceAllocation*) object;
D_DEBUG_AT( Core_SurfAllocation, "destroying %p (size %d)\n", allocation, allocation->size );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
CoreSurfaceAllocation_Deinit_Dispatch( &allocation->call );
if (!D_FLAGS_IS_SET(allocation->flags, CSALF_INITIALIZING)) {
if (allocation->surface)
dfb_surface_lock( allocation->surface );
CORE_SURFACE_ALLOCATION_ASSERT( allocation );
dfb_surface_pool_deallocate( allocation->pool, allocation );
if (allocation->surface)
dfb_surface_unlock( allocation->surface );
}
if (allocation->data)
SHFREE( allocation->pool->shmpool, allocation->data );
if (allocation->read_tasks)
delete allocation->read_tasks;
direct_serial_deinit( &allocation->serial );
D_MAGIC_CLEAR( allocation );
fusion_object_destroy( object );
}
FusionObjectPool *
dfb_surface_allocation_pool_create( const FusionWorld *world )
{
FusionObjectPool *pool;
pool = fusion_object_pool_create( "Surface Allocation Pool",
sizeof(CoreSurfaceAllocation),
sizeof(CoreSurfaceAllocationNotification),
surface_allocation_destructor, NULL, world );
return pool;
}
/**********************************************************************************************************************/
DFBResult
dfb_surface_allocation_create( CoreDFB *core,
CoreSurfaceBuffer *buffer,
CoreSurfacePool *pool,
CoreSurfaceAllocation **ret_allocation )
{
DFBResult ret;
CoreSurface *surface;
CoreSurfaceAllocation *allocation;
D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );
D_ASSERT( pool != NULL );
D_ASSERT( ret_allocation != NULL );
D_DEBUG_AT( Core_SurfAllocation, "%s( %dx%d %s )\n", __FUNCTION__,
buffer->config.size.w, buffer->config.size.h, dfb_pixelformat_name( buffer->config.format ) );
surface = buffer->surface;
D_MAGIC_ASSERT( surface, CoreSurface );
allocation = dfb_core_create_surface_allocation( core );
if (!allocation)
return DFB_FUSION;
allocation->buffer = buffer;
allocation->surface = surface;
allocation->pool = pool;
allocation->access = pool->desc.access;
allocation->config = buffer->config;
allocation->type = buffer->type;
allocation->resource_id = buffer->resource_id;
allocation->flags = CSALF_INITIALIZING;
allocation->index = buffer->index;
allocation->buffer_id = buffer->object.id;
if (pool->alloc_data_size) {
allocation->data = SHCALLOC( pool->shmpool, 1, pool->alloc_data_size );
if (!allocation->data) {
ret = (DFBResult) D_OOSHM();
goto error;
}
}
direct_serial_init( &allocation->serial );
fusion_ref_add_permissions( &allocation->object.ref, 0, FUSION_REF_PERMIT_REF_UNREF_LOCAL );
CoreSurfaceAllocation_Init_Dispatch( core, allocation, &allocation->call );
D_MAGIC_SET( allocation, CoreSurfaceAllocation );
fusion_object_activate( &allocation->object );
*ret_allocation = allocation;
D_DEBUG_AT( Core_SurfAllocation, "%s(): allocation=%p\n", __FUNCTION__, (void *)allocation );
return DFB_OK;
error:
if (allocation->data)
SHFREE( pool->shmpool, allocation->data );
fusion_object_destroy( &allocation->object );
return ret;
}
DFBResult
dfb_surface_allocation_decouple( CoreSurfaceAllocation *allocation )
{
int i;
int locks;
CoreSurfaceBuffer *buffer;
CoreSurfaceAllocation *alloc;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p )\n", __FUNCTION__, allocation );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
buffer = allocation->buffer;
D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );
D_MAGIC_ASSERT( allocation->surface, CoreSurface );
D_ASSERT( allocation->surface == allocation->buffer->surface );
/* Indicate that this surface buffer pool allocation is about to be destroyed. */
dfb_surface_pool_notify( allocation->surface, buffer, allocation, CSNF_BUFFER_ALLOCATION_DESTROY );
allocation->buffer = NULL;
allocation->surface = NULL;
fusion_vector_remove( &buffer->allocs, fusion_vector_index_of( &buffer->allocs, allocation ) );
locks = dfb_surface_allocation_locks( allocation );
if (!locks) {
if (allocation->accessed[CSAID_GPU] & (CSAF_READ | CSAF_WRITE))
/* ...wait for the operation to finish. */
dfb_gfxcard_wait_serial( &allocation->gfx_serial );
dfb_surface_pool_deallocate( allocation->pool, allocation );
}
/* Reset 'read' allocation pointer of buffer */
if (buffer->read == allocation)
buffer->read = NULL;
/* Update 'written' allocation pointer of buffer */
if (buffer->written == allocation) {
/* Reset pointer first */
buffer->written = NULL;
/* Iterate through remaining allocations */
fusion_vector_foreach (alloc, i, buffer->allocs) {
CORE_SURFACE_ALLOCATION_ASSERT( alloc );
/* Check if allocation is up to date and set it as 'written' allocation */
if (direct_serial_check( &alloc->serial, &buffer->serial )) {
buffer->written = alloc;
break;
}
}
}
dfb_surface_allocation_unlink( &allocation );
return DFB_OK;
}
/**********************************************************************************************************************/
static void
transfer_buffer( const CoreSurfaceConfig *config,
const char *src,
char *dst,
int srcpitch,
int dstpitch )
{
int i;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p, %p [%d] -> %p [%d] ) * %d\n",
__FUNCTION__, config, src, srcpitch, dst, dstpitch, config->size.h );
D_ASSERT( src != NULL );
D_ASSERT( dst != NULL );
D_ASSERT( srcpitch > 0 );
D_ASSERT( dstpitch > 0 );
D_ASSERT( srcpitch >= DFB_BYTES_PER_LINE( config->format, config->size.w ) );
D_ASSERT( dstpitch >= DFB_BYTES_PER_LINE( config->format, config->size.w ) );
for (i=0; i<config->size.h; i++) {
direct_memcpy( dst, src, DFB_BYTES_PER_LINE( config->format, config->size.w ) );
src += srcpitch;
dst += dstpitch;
}
switch (config->format) {
case DSPF_YV12:
case DSPF_I420:
for (i=0; i<config->size.h; i++) {
direct_memcpy( dst, src,
DFB_BYTES_PER_LINE( config->format, config->size.w / 2 ) );
src += srcpitch / 2;
dst += dstpitch / 2;
}
break;
case DSPF_YV16:
for (i=0; i<config->size.h*2; i++) {
direct_memcpy( dst, src,
DFB_BYTES_PER_LINE( config->format, config->size.w / 2 ) );
src += srcpitch / 2;
dst += dstpitch / 2;
}
break;
case DSPF_NV12:
case DSPF_NV21:
for (i=0; i<config->size.h/2; i++) {
direct_memcpy( dst, src,
DFB_BYTES_PER_LINE( config->format, config->size.w ) );
src += srcpitch;
dst += dstpitch;
}
break;
case DSPF_NV16:
for (i=0; i<config->size.h; i++) {
direct_memcpy( dst, src,
DFB_BYTES_PER_LINE( config->format, config->size.w ) );
src += srcpitch;
dst += dstpitch;
}
break;
case DSPF_YUV444P:
for (i=0; i<config->size.h*2; i++) {
direct_memcpy( dst, src,
DFB_BYTES_PER_LINE( config->format, config->size.w ) );
src += srcpitch;
dst += dstpitch;
}
break;
default:
break;
}
}
static DFBResult
allocation_update_copy( CoreSurfaceAllocation *allocation,
CoreSurfaceAllocation *source )
{
DFBResult ret;
CoreSurfaceBufferLock src;
CoreSurfaceBufferLock dst;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p )\n", __FUNCTION__, (void *)allocation);
D_ASSERT( allocation != source );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
D_MAGIC_ASSERT( source, CoreSurfaceAllocation );
/* Lock the source allocation. */
dfb_surface_buffer_lock_init( &src, CSAID_CPU, CSAF_READ );
dfb_surface_pool_prelock( source->pool, source, CSAID_CPU, CSAF_READ );
ret = dfb_surface_pool_lock( source->pool, source, &src );
if (ret) {
D_DERROR( ret, "Core/SurfBuffer: Could not lock source for transfer!\n" );
dfb_surface_buffer_lock_deinit( &src );
return ret;
}
/* Lock the destination allocation. */
dfb_surface_buffer_lock_init( &dst, CSAID_CPU, CSAF_WRITE );
dfb_surface_pool_prelock( allocation->pool, allocation, CSAID_CPU, CSAF_WRITE );
/*
* Track that the CPU wrote to the destination buffer allocation and that it read
* from the source buffer allocation so that proper cache flushing will occur.
*/
if (!dfb_config->task_manager) {
allocation->accessed[CSAID_CPU] = (CoreSurfaceAccessFlags)(allocation->accessed[CSAID_CPU] | CSAF_WRITE);
source->accessed[CSAID_CPU] = (CoreSurfaceAccessFlags)(source->accessed[CSAID_CPU] | CSAF_READ);
}
ret = dfb_surface_pool_lock( allocation->pool, allocation, &dst );
if (ret) {
D_DERROR( ret, "Core/SurfBuffer: Could not lock destination for transfer!\n" );
dfb_surface_pool_unlock( source->pool, source, &src );
dfb_surface_buffer_lock_deinit( &dst );
dfb_surface_buffer_lock_deinit( &src );
return ret;
}
transfer_buffer( &allocation->config, (char*) src.addr, (char*) dst.addr, src.pitch, dst.pitch );
dfb_surface_pool_unlock( allocation->pool, allocation, &dst );
dfb_surface_pool_unlock( source->pool, source, &src );
dfb_surface_buffer_lock_deinit( &dst );
dfb_surface_buffer_lock_deinit( &src );
return DFB_OK;
}
static DFBResult
allocation_update_write( CoreSurfaceAllocation *allocation,
CoreSurfaceAllocation *source )
{
DFBResult ret;
CoreSurfaceBufferLock src;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p )\n", __FUNCTION__, (void *)allocation );
D_ASSERT( allocation != source );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
D_MAGIC_ASSERT( source, CoreSurfaceAllocation );
/* Lock the source allocation. */
dfb_surface_buffer_lock_init( &src, CSAID_CPU, CSAF_READ );
dfb_surface_pool_prelock( source->pool, source, CSAID_CPU, CSAF_READ );
/*
* Track that the CPU wrote to the destination buffer allocation and that it read
* from the source buffer allocation so that proper cache flushing will occur.
*/
if (!dfb_config->task_manager)
source->accessed[CSAID_CPU] = (CoreSurfaceAccessFlags)(source->accessed[CSAID_CPU] | CSAF_READ);
ret = dfb_surface_pool_lock( source->pool, source, &src );
if (ret) {
D_DERROR( ret, "Core/SurfBuffer: Could not lock source for transfer!\n" );
dfb_surface_buffer_lock_deinit( &src );
return ret;
}
/* Write to the destination allocation. */
ret = dfb_surface_pool_write( allocation->pool, allocation, (char*) src.addr, src.pitch, NULL );
if (ret)
D_DERROR( ret, "Core/SurfBuffer: Could not write from destination allocation!\n" );
dfb_surface_pool_unlock( source->pool, source, &src );
dfb_surface_buffer_lock_deinit( &src );
return ret;
}
static DFBResult
allocation_update_read( CoreSurfaceAllocation *allocation,
CoreSurfaceAllocation *source )
{
DFBResult ret;
CoreSurfaceBufferLock dst;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p )\n", __FUNCTION__, (void *)allocation );
D_ASSERT( allocation != source );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
D_MAGIC_ASSERT( source, CoreSurfaceAllocation );
/* Lock the destination allocation. */
dfb_surface_buffer_lock_init( &dst, CSAID_CPU, CSAF_WRITE );
dfb_surface_pool_prelock( allocation->pool, allocation, CSAID_CPU, CSAF_WRITE );
/*
* Track that the CPU wrote to the destination buffer allocation and that it read
* from the source buffer allocation so that proper cache flushing will occur.
*/
if (!dfb_config->task_manager)
allocation->accessed[CSAID_CPU] = (CoreSurfaceAccessFlags)(allocation->accessed[CSAID_CPU] | CSAF_READ);
ret = dfb_surface_pool_lock( allocation->pool, allocation, &dst );
if (ret) {
D_DERROR( ret, "Core/SurfBuffer: Could not lock destination for transfer!\n" );
dfb_surface_buffer_lock_deinit( &dst );
return ret;
}
/* Read from the source allocation. */
ret = dfb_surface_pool_read( source->pool, source, dst.addr, dst.pitch, NULL );
if (ret)
D_DERROR( ret, "Core/SurfBuffer: Could not read from source allocation!\n" );
dfb_surface_pool_unlock( allocation->pool, allocation, &dst );
dfb_surface_buffer_lock_deinit( &dst );
return ret;
}
namespace DirectFB {
class TransferTask : public SurfaceTask
{
public:
TransferTask( CoreSurfaceAllocation *allocation,
CoreSurfaceAllocation *source )
:
SurfaceTask( CSAID_CPU ), // FIXME
allocation( allocation ),
source( source )
{
D_ASSUME( allocation != source );
D_ASSERT( source->buffer == allocation->buffer );
buffer = source->buffer;
dfb_surface_buffer_ref( buffer );
}
virtual ~TransferTask()
{
dfb_surface_buffer_unref( buffer );
}
static DFBResult Generate( CoreSurfaceAllocation *allocation,
CoreSurfaceAllocation *source )
{
TransferTask *task = new TransferTask( allocation, source );
task->AddAccess( allocation, CSAF_WRITE );
task->AddAccess( source, CSAF_READ );
task->Flush();
return DFB_OK;
}
protected:
virtual DFBResult Run()
{
DFBResult ret;
D_DEBUG_AT( Core_SurfAllocation, " -> updating allocation %p from %p...\n", allocation, source );
D_MAGIC_ASSERT( source, CoreSurfaceAllocation );
ret = dfb_surface_pool_bridges_transfer( buffer, source, allocation, NULL, 0 );
if (ret) {
if ((source->access[CSAID_CPU] & CSAF_READ) && (allocation->access[CSAID_CPU] & CSAF_WRITE))
ret = allocation_update_copy( allocation, source );
else if (source->access[CSAID_CPU] & CSAF_READ)
ret = allocation_update_write( allocation, source );
else if (allocation->access[CSAID_CPU] & CSAF_WRITE)
ret = allocation_update_read( allocation, source );
else {
D_UNIMPLEMENTED();
ret = DFB_UNSUPPORTED;
}
}
if (ret) {
D_DERROR( ret, "Core/SurfaceBuffer: Updating allocation failed!\n" );
return ret;
}
Done();
return DFB_OK;
}
private:
CoreSurfaceAllocation *allocation;
CoreSurfaceAllocation *source;
CoreSurfaceBuffer *buffer;
};
extern "C" {
DFBResult
dfb_surface_allocation_update( CoreSurfaceAllocation *allocation,
CoreSurfaceAccessFlags access )
{
DFBResult ret;
int i;
CoreSurfaceAllocation *alloc;
CoreSurfaceBuffer *buffer;
D_DEBUG_AT( Core_SurfAllocation, "%s( %p )\n", __FUNCTION__, (void *)allocation );
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
D_FLAGS_ASSERT( access, CSAF_ALL );
D_DEBUG_AT( Core_SurfAllocation, " -> alloc: %s\n", ToString_CoreSurfaceAllocation( allocation ) );
buffer = allocation->buffer;
D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );
D_MAGIC_ASSERT( buffer->surface, CoreSurface );
FUSION_SKIRMISH_ASSERT( &buffer->surface->lock );
if (direct_serial_update( &allocation->serial, &buffer->serial ) && buffer->written) {
CoreSurfaceAllocation *source = buffer->written;
D_DEBUG_AT( Core_SurfAllocation, " -> written: %s\n", ToString_CoreSurfaceAllocation( source ) );
D_ASSUME( allocation != source );
D_DEBUG_AT( Core_SurfAllocation, " -> alloc/written buffer: %p/%p\n", allocation->buffer, source->buffer );
D_MAGIC_ASSERT( source, CoreSurfaceAllocation );
D_ASSERT( source->buffer == allocation->buffer );
if (dfb_config->task_manager) {
DirectFB::TransferTask::Generate( allocation, source );
}
else {
D_DEBUG_AT( Core_SurfAllocation, " -> updating allocation %p from %p...\n", allocation, source );
ret = dfb_surface_pool_bridges_transfer( buffer, source, allocation, NULL, 0 );
if (ret) {
if ((source->access[CSAID_CPU] & CSAF_READ) && (allocation->access[CSAID_CPU] & CSAF_WRITE))
ret = allocation_update_copy( allocation, source );
else if (source->access[CSAID_CPU] & CSAF_READ)
ret = allocation_update_write( allocation, source );
else if (allocation->access[CSAID_CPU] & CSAF_WRITE)
ret = allocation_update_read( allocation, source );
else {
D_UNIMPLEMENTED();
ret = DFB_UNSUPPORTED;
}
}
if (ret) {
D_DERROR( ret, "Core/SurfaceBuffer: Updating allocation failed!\n" );
return ret;
}
}
}
if (access & CSAF_WRITE) {
D_DEBUG_AT( Core_SurfAllocation, " -> increasing serial...\n" );
direct_serial_increase( &buffer->serial );
direct_serial_copy( &allocation->serial, &buffer->serial );
buffer->written = allocation;
buffer->read = NULL;
/* Zap volatile allocations (freed when no longer up to date). */
fusion_vector_foreach (alloc, i, buffer->allocs) {
D_MAGIC_ASSERT( alloc, CoreSurfaceAllocation );
if (alloc != allocation && (alloc->flags & CSALF_VOLATILE)) {
dfb_surface_allocation_decouple( alloc );
i--;
}
}
}
else
buffer->read = allocation;
/* Zap all other allocations? */
if (dfb_config->thrifty_surface_buffers) {
buffer->written = buffer->read = allocation;
fusion_vector_foreach (alloc, i, buffer->allocs) {
D_MAGIC_ASSERT( alloc, CoreSurfaceAllocation );
/* Don't zap preallocated which would not really free up memory, but just loose the handle. */
if (alloc != allocation && !(alloc->flags & (CSALF_PREALLOCATED | CSALF_MUCKOUT))) {
dfb_surface_allocation_decouple( alloc );
i--;
}
}
}
return DFB_OK;
}
DFBResult
dfb_surface_allocation_dump( CoreSurfaceAllocation *allocation,
const char *directory,
const char *prefix,
bool raw )
{
DFBResult ret = DFB_OK;
CoreSurfacePool *pool;
D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
pool = allocation->pool;
D_MAGIC_ASSERT( pool, CoreSurfacePool );
D_ASSERT( directory != NULL );
D_DEBUG_AT( Core_SurfAllocation, "%s( %p, '%s', '%s' )\n", __FUNCTION__, (void *)allocation, directory, prefix );
if (D_FLAGS_IS_SET( pool->desc.caps, CSPCAPS_READ )) {
int pitch;
int size;
void *buf;
dfb_surface_calc_buffer_size( allocation->surface, 4, 1, &pitch, &size );
buf = D_MALLOC( size );
if (!buf)
return (DFBResult) D_OOM();
ret = dfb_surface_pool_read( pool, allocation, buf, pitch, NULL );
if (ret == DFB_OK)
ret = dfb_surface_buffer_dump_type_locked2( allocation->buffer, directory, prefix, raw, buf, pitch );
D_FREE( buf );
}
else {
CoreSurfaceBufferLock lock;
dfb_surface_buffer_lock_init( &lock, CSAID_CPU, CSAF_READ );
/* Lock the surface buffer, get the data pointer and pitch. */
ret = dfb_surface_pool_lock( pool, allocation, &lock );
if (ret) {
dfb_surface_buffer_lock_deinit( &lock );
return ret;
}
ret = dfb_surface_buffer_dump_type_locked( allocation->buffer, directory, prefix, raw, &lock );
/* Unlock the surface buffer. */
dfb_surface_pool_unlock( allocation->pool, allocation, &lock );
dfb_surface_buffer_lock_deinit( &lock );
}
return ret;
}
}
}