pack_buffer.c revision d0a71f63bd810b54e0359223fe53b07730154dc5
/* Copyright (c) 2001, Stanford University
* All rights reserved
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include "cr_mem.h"
#include "cr_string.h"
#include "packer.h"
#include "cr_error.h"
#include "cr_protocol.h"
#ifndef IN_RING0
#include "cr_unpack.h"
#endif
#ifndef IN_RING0
void crWriteUnalignedDouble( void *buffer, double d )
{
unsigned int *ui = (unsigned int *) buffer;
ui[0] = ((unsigned int *) &d)[0];
ui[1] = ((unsigned int *) &d)[1];
}
void crWriteSwappedDouble( void *buffer, double d )
{
unsigned int *ui = (unsigned int *) buffer;
ui[0] = SWAP32(((unsigned int *) &d)[1]);
ui[1] = SWAP32(((unsigned int *) &d)[0]);
}
double crReadUnalignedDouble( const void *buffer )
{
const unsigned int *ui = (unsigned int *) buffer;
double d;
((unsigned int *) &d)[0] = ui[0];
((unsigned int *) &d)[1] = ui[1];
return d;
}
#endif
/*
* We need the packer to run as efficiently as possible. To avoid one
* pointer dereference from the CRPackContext to the current CRPackBuffer,
* we keep a _copy_ of the current CRPackBuffer in the CRPackContext and
* operate on the fields in CRPackContext, rather than the CRPackBuffer.
*
* To keep things in sync, when we change a context's
* buffer, we have to use the crPackSet/GetBuffer() functions.
*/
void crPackSetBuffer( CRPackContext *pc, CRPackBuffer *buffer )
{
CRASSERT( pc );
CRASSERT( buffer );
if (pc->currentBuffer == buffer)
return; /* re-bind is no-op */
if (pc->currentBuffer) {
/* Another buffer currently bound to this packer (shouldn't normally occur)
* Release it. Fixes Ensight issue.
*/
crPackReleaseBuffer(pc);
}
CRASSERT( pc->currentBuffer == NULL); /* release if NULL? */
CRASSERT( buffer->context == NULL );
/* bind context to buffer */
pc->currentBuffer = buffer;
buffer->context = pc;
/* update the context's packing fields with those from the buffer */
pc->buffer = *buffer; /* struct copy */
}
#ifndef IN_RING0
/* This is useful for debugging packer problems */
void crPackSetBufferDEBUG( const char *file, int line,
CRPackContext *pc, CRPackBuffer *buffer)
{
crPackSetBuffer( pc, buffer );
/* record debugging info */
pc->file = crStrdup(file);
pc->line = line;
}
#endif
/*
* Release the buffer currently attached to the context.
* Update/resync data structures.
*/
void crPackReleaseBuffer( CRPackContext *pc )
{
CRPackBuffer *buf;
CRASSERT( pc );
if (!pc->currentBuffer) {
crWarning("crPackReleaseBuffer called with no current buffer");
return; /* nothing to do */
}
CRASSERT( pc->currentBuffer->context == pc );
/* buffer to release */
buf = pc->currentBuffer;
/* copy context's fields back into the buffer to update it */
*buf = pc->buffer; /* struct copy */
/* unbind buffer from context */
buf->context = NULL;
pc->currentBuffer = NULL;
/* zero-out context's packing fields just to be safe */
crMemZero(&(pc->buffer), sizeof(pc->buffer));
/* update the debugging fields */
if (pc->file)
crFree(pc->file);
pc->file = NULL;
pc->line = -1;
}
void crPackFlushFunc( CRPackContext *pc, CRPackFlushFunc ff )
{
pc->Flush = ff;
}
void crPackFlushArg( CRPackContext *pc, void *flush_arg )
{
pc->flush_arg = flush_arg;
}
void crPackSendHugeFunc( CRPackContext *pc, CRPackSendHugeFunc shf )
{
pc->SendHuge = shf;
}
/*
* This basically resets the buffer attached to <pc> to the default, empty
* state.
*/
void crPackResetPointers( CRPackContext *pc )
{
const GLboolean geom_only = pc->buffer.geometry_only; /* save this flag */
const GLboolean holds_BeginEnd = pc->buffer.holds_BeginEnd;
const GLboolean in_BeginEnd = pc->buffer.in_BeginEnd;
const GLboolean canBarf = pc->buffer.canBarf;
CRPackBuffer *buf = pc->currentBuffer;
CRASSERT(buf);
crPackInitBuffer( buf, buf->pack, buf->size, buf->mtu );
pc->buffer.geometry_only = geom_only; /* restore the flag */
pc->buffer.holds_BeginEnd = holds_BeginEnd;
pc->buffer.in_BeginEnd = in_BeginEnd;
pc->buffer.canBarf = canBarf;
}
/**
* Return max number of opcodes that'll fit in the given buffer size.
* Each opcode has at least a 1-word payload, so opcodes can occupy at most
* 20% of the space.
*/
int
crPackMaxOpcodes( int buffer_size )
{
int n = ( buffer_size - sizeof(CRMessageOpcodes) ) / 5;
/* Don't forget to add one here in case the buffer size is not
* divisible by 4. Thanks to Ken Moreland for finding this.
*/
n++;
/* round up to multiple of 4 */
n = (n + 0x3) & (~0x3);
return n;
}
/**
* Return max number of data bytes that'll fit in the given buffer size.
*/
int
crPackMaxData( int buffer_size )
{
int n = buffer_size - sizeof(CRMessageOpcodes);
n -= crPackMaxOpcodes(buffer_size);
return n;
}
/**
* Initialize the given CRPackBuffer object.
* The buffer may or may not be currently bound to a CRPackContext.
*
* Opcodes and operands are packed into a buffer in a special way.
* Opcodes start at opcode_start and go downward in memory while operands
* start at data_start and go upward in memory. The buffer is full when we
* either run out of opcode space or operand space.
*
* Diagram (memory addresses increase upward):
*
* data_end -> | | <- buf->pack + buf->size
* +---------+
* | |
* | |
* | operands|
* | |
* | |
* data_start -> +---------+
* opcode_start -> | |
* | |
* | opcodes |
* | |
* | |
* opcode_end -> +---------+ <- buf->pack
*
* \param buf the CRPackBuffer to initialize
* \param pack the address of the buffer for packing opcodes/operands.
* \param size size of the buffer, in bytes
* \param mtu max transmission unit size, in bytes. When the buffer
* has 'mtu' bytes in it, we have to send it. The MTU might
* be somewhat smaller than the buffer size.
*/
void crPackInitBuffer( CRPackBuffer *buf, void *pack, int size, int mtu )
{
unsigned int num_opcodes;
CRASSERT(mtu <= size);
buf->size = size;
buf->mtu = mtu;
buf->pack = pack;
num_opcodes = crPackMaxOpcodes( buf->size );
buf->data_start =
(unsigned char *) buf->pack + num_opcodes + sizeof(CRMessageOpcodes);
buf->data_current = buf->data_start;
buf->data_end = (unsigned char *) buf->pack + buf->size;
buf->opcode_start = buf->data_start - 1;
buf->opcode_current = buf->opcode_start;
buf->opcode_end = buf->opcode_start - num_opcodes;
buf->geometry_only = GL_FALSE;
buf->holds_BeginEnd = GL_FALSE;
buf->in_BeginEnd = GL_FALSE;
buf->canBarf = GL_FALSE;
if (buf->context) {
/* Also reset context's packing fields */
CRPackContext *pc = buf->context;
CRASSERT(pc->currentBuffer == buf);
/*crMemcpy( &(pc->buffer), buf, sizeof(*buf) );*/
pc->buffer = *buf;
}
}
int crPackCanHoldBuffer( const CRPackBuffer *src )
{
const int num_data = crPackNumData(src);
const int num_opcode = crPackNumOpcodes(src);
int res;
CR_GET_PACKER_CONTEXT(pc);
CR_LOCK_PACKER_CONTEXT(pc);
res = crPackCanHoldOpcode( pc, num_opcode, num_data );
CR_UNLOCK_PACKER_CONTEXT(pc);
return res;
}
int crPackCanHoldBoundedBuffer( const CRPackBuffer *src )
{
const int len_aligned = (src->data_current - src->opcode_current - 1 + 3) & ~3;
CR_GET_PACKER_CONTEXT(pc);
/* 24 is the size of the bounds-info packet... */
return crPackCanHoldOpcode( pc, 1, len_aligned + 24 );
}
void crPackAppendBuffer( const CRPackBuffer *src )
{
CR_GET_PACKER_CONTEXT(pc);
const int num_data = crPackNumData(src);
const int num_opcode = crPackNumOpcodes(src);
CRASSERT(num_data >= 0);
CRASSERT(num_opcode >= 0);
CR_LOCK_PACKER_CONTEXT(pc);
/* don't append onto ourself! */
CRASSERT(pc->currentBuffer);
CRASSERT(pc->currentBuffer != src);
if (!crPackCanHoldBuffer(src))
{
if (src->holds_BeginEnd)
{
crWarning( "crPackAppendBuffer: overflowed the destination!" );
CR_UNLOCK_PACKER_CONTEXT(pc);
return;
}
else
{
crError( "crPackAppendBuffer: overflowed the destination!" );
CR_UNLOCK_PACKER_CONTEXT(pc);
}
}
/* Copy the buffer data/operands which are at the head of the buffer */
crMemcpy( pc->buffer.data_current, src->data_start, num_data );
pc->buffer.data_current += num_data;
/* Copy the buffer opcodes which are at the tail of the buffer */
CRASSERT( pc->buffer.opcode_current - num_opcode >= pc->buffer.opcode_end );
crMemcpy( pc->buffer.opcode_current + 1 - num_opcode, src->opcode_current + 1,
num_opcode );
pc->buffer.opcode_current -= num_opcode;
pc->buffer.holds_BeginEnd |= src->holds_BeginEnd;
pc->buffer.in_BeginEnd = src->in_BeginEnd;
pc->buffer.holds_List |= src->holds_List;
CR_UNLOCK_PACKER_CONTEXT(pc);
}
void
crPackAppendBoundedBuffer( const CRPackBuffer *src, const CRrecti *bounds )
{
CR_GET_PACKER_CONTEXT(pc);
const GLbyte *payload = (const GLbyte *) src->opcode_current + 1;
const int num_opcodes = crPackNumOpcodes(src);
const int length = src->data_current - src->opcode_current - 1;
CRASSERT(pc);
CR_LOCK_PACKER_CONTEXT(pc);
CRASSERT(pc->currentBuffer);
CRASSERT(pc->currentBuffer != src);
/*
* payload points to the block of opcodes immediately followed by operands.
*/
if ( !crPackCanHoldBoundedBuffer( src ) )
{
if (src->holds_BeginEnd)
{
crWarning( "crPackAppendBoundedBuffer: overflowed the destination!" );
CR_UNLOCK_PACKER_CONTEXT(pc);
return;
}
else
{
crError( "crPackAppendBoundedBuffer: overflowed the destination!" );
CR_UNLOCK_PACKER_CONTEXT(pc);
}
}
if (pc->swapping)
crPackBoundsInfoCRSWAP( bounds, payload, length, num_opcodes );
else
crPackBoundsInfoCR( bounds, payload, length, num_opcodes );
pc->buffer.holds_BeginEnd |= src->holds_BeginEnd;
pc->buffer.in_BeginEnd = src->in_BeginEnd;
pc->buffer.holds_List |= src->holds_List;
CR_UNLOCK_PACKER_CONTEXT(pc);
}
#ifndef CHROMIUM_THREADSAFE
static unsigned char *sanityCheckPointer = NULL;
#endif
/*
* Allocate space for a command that might be very large, such as
* glTexImage2D or glBufferDataARB call.
* The command buffer _MUST_ then be transmitted by calling crHugePacket.
*/
void *crPackAlloc( unsigned int size )
{
CR_GET_PACKER_CONTEXT(pc);
unsigned char *data_ptr;
/* include space for the length and make the payload word-aligned */
size = ( size + sizeof(unsigned int) + 0x3 ) & ~0x3;
CR_LOCK_PACKER_CONTEXT(pc);
if ( crPackCanHoldOpcode( pc, 1, size ) )
{
/* we can just put it in the current buffer */
CR_GET_BUFFERED_POINTER_NOLOCK(pc, size ); /* NOTE: this sets data_ptr */
}
else
{
/* Okay, it didn't fit. Maybe it will after we flush. */
CR_UNLOCK_PACKER_CONTEXT(pc);
pc->Flush( pc->flush_arg );
CR_LOCK_PACKER_CONTEXT(pc);
if ( crPackCanHoldOpcode( pc, 1, size ) )
{
CR_GET_BUFFERED_POINTER_NOLOCK(pc, size ); /* NOTE: this sets data_ptr */
}
else
{
/* It's really way too big, so allocate a temporary packet
* with space for the single opcode plus the payload &
* header.
*/
data_ptr = (unsigned char *)
crAlloc( sizeof(CRMessageOpcodes) + 4 + size );
/* skip the header & opcode space */
data_ptr += sizeof(CRMessageOpcodes) + 4;
}
}
/* At the top of the function, we added four to the request size and
* rounded it up to the next multiple of four.
*
* At this point, we have:
*
* HIGH MEM | byte size - 1 | \
* ... |
* ... | - original 'size' bytes for data
* | operand data | |
* return value -> | operand data | /
* | byte 3 | \
* | byte 2 | |- These bytes will store 'size'
* | byte 1 | |
* data_ptr -> | byte 0 | /
* | CR opcode | <- Set in packspuHuge()
* | unused |
* | unused |
* | unused |
* | CRMessageOpcodes |
* | CRMessageOpcodes |
* ...
* | CRMessageOpcodes |
* | CRMessageOpcodes |
* LOW MEM +------------------+
*/
if (pc->swapping)
{
*((unsigned int *) data_ptr) = SWAP32(size);
crDebug( "Just swapped the length, putting %d on the wire!", *((unsigned int *) data_ptr));
}
else
{
*((unsigned int *) data_ptr) = size;
}
#ifndef CHROMIUM_THREADSAFE
sanityCheckPointer = data_ptr + 4;
#endif
return data_ptr + 4;
}
#define IS_BUFFERED( packet ) \
((unsigned char *) (packet) >= pc->buffer.data_start && \
(unsigned char *) (packet) < pc->buffer.data_end)
/*
* Transmit a packet which was allocated with crPackAlloc.
*/
void crHugePacket( CROpcode opcode, void *packet )
{
CR_GET_PACKER_CONTEXT(pc);
#ifndef CHROMIUM_THREADSAFE
CRASSERT(sanityCheckPointer == packet);
sanityCheckPointer = NULL;
#endif
if ( IS_BUFFERED( packet ) )
WRITE_OPCODE( pc, opcode );
else
pc->SendHuge( opcode, packet );
}
void crPackFree( void *packet )
{
CR_GET_PACKER_CONTEXT(pc);
if ( IS_BUFFERED( packet ) )
{
CR_UNLOCK_PACKER_CONTEXT(pc);
return;
}
CR_UNLOCK_PACKER_CONTEXT(pc);
/* the pointer passed in doesn't include the space for the single
* opcode (4 bytes because of the alignment requirement) or the
* length field or the header */
crFree( (unsigned char *) packet - 8 - sizeof(CRMessageOpcodes) );
}
void crNetworkPointerWrite( CRNetworkPointer *dst, void *src )
{
/* init CRNetworkPointer with invalid values */
dst->ptrAlign[0] = 0xDeadBeef;
dst->ptrAlign[1] = 0xCafeBabe;
/* copy the pointer's value into the CRNetworkPointer */
crMemcpy( dst, &src, sizeof(src) );
/* if either assertion fails, it probably means that a packer function
* (which returns a value) was called without setting up the writeback
* pointer, or something like that.
*/
CRASSERT(dst->ptrAlign[0] != 0xffffffff);
CRASSERT(dst->ptrAlign[0] != 0xDeadBeef);
}