Ejemplo n.º 1
0
/*
 * This is called from either the Pack SPU and the packer library whenever
 * we need to send a data buffer to the server.
 */
void
packspuFlush(void *arg )
{
	ThreadInfo *thread = (ThreadInfo *) arg;
	ContextInfo *ctx;
	unsigned int len;
	CRMessageOpcodes *hdr;
	CRPackBuffer *buf;

	/* we should _always_ pass a valid <arg> value */
	CRASSERT(thread);
	ctx = thread->currentContext;
	buf = &(thread->buffer);
	CRASSERT(buf);

	/* We're done packing into the current buffer, unbind it */
	crPackReleaseBuffer( thread->packer );

	/*
	printf("%s thread=%p thread->id = %d thread->pc=%p t2->id=%d t2->pc=%p packbuf=%p packbuf=%p\n",
		   __FUNCTION__, (void*) thread, (int) thread->id, thread->packer,
		   (int) t2->id, t2->packer,
		   buf->pack, thread->packer->buffer.pack);
	*/

	if ( buf->opcode_current == buf->opcode_start ) {
           /*
           printf("%s early return\n", __FUNCTION__);
           */
           /* XXX these calls seem to help, but might be appropriate */
           crPackSetBuffer( thread->packer, buf );
           crPackResetPointers(thread->packer);
           return;
	}

	hdr = __prependHeader( buf, &len, 0 );

	CRASSERT( thread->netServer.conn );

	if ( buf->holds_BeginEnd )
		crNetBarf( thread->netServer.conn, &(buf->pack), hdr, len );
	else
		crNetSend( thread->netServer.conn, &(buf->pack), hdr, len );

	buf->pack = crNetAlloc( thread->netServer.conn );

	/* The network may have found a new mtu */
	buf->mtu = thread->netServer.conn->mtu;

	crPackSetBuffer( thread->packer, buf );

	crPackResetPointers(thread->packer);
	(void) arg;
}
Ejemplo n.º 2
0
/**
 * Send the current tilesort tile info to all the servers.
 */
void
tilesortspuSendTileInfoToServers( WindowInfo *winInfo )
{
	GET_THREAD(thread);
	int i;

	/* release geometry buffer */
	crPackReleaseBuffer( thread->packer );

	/* loop over servers */
	for (i = 0; i < tilesort_spu.num_servers; i++)
	{
		ServerWindowInfo *servWinInfo = winInfo->server + i;
		int tileInfo[4 + 4 * CR_MAX_EXTENTS], arraySize;
		int j;

		/* build tileInfo array */
		tileInfo[0] = i;
		tileInfo[1] = winInfo->muralWidth;
		tileInfo[2] = winInfo->muralHeight;
		tileInfo[3] = servWinInfo->num_extents;
		for (j = 0; j < servWinInfo->num_extents; j++)
		{
			int w = servWinInfo->extents[j].x2 - servWinInfo->extents[j].x1;
			int h = servWinInfo->extents[j].y2 - servWinInfo->extents[j].y1;
			tileInfo[4 + j * 4 + 0] = servWinInfo->extents[j].x1;
			tileInfo[4 + j * 4 + 1] = servWinInfo->extents[j].y1;
			tileInfo[4 + j * 4 + 2] = w;
			tileInfo[4 + j * 4 + 3] = h;
		}
		arraySize = 4 + 4 * servWinInfo->num_extents;

		/* pack/send to server[i] */
		crPackSetBuffer( thread->packer, &(thread->buffer[i]) );

		if (tilesort_spu.swap)
			crPackChromiumParametervCRSWAP(GL_TILE_INFO_CR, GL_INT,
										   arraySize, tileInfo);
		else
			crPackChromiumParametervCR(GL_TILE_INFO_CR, GL_INT,
									   arraySize, tileInfo);

		/* release server buffer */
		crPackReleaseBuffer( thread->packer );
	}

	/* Restore default buffer */
	crPackSetBuffer( thread->packer, &(thread->geometry_buffer) );
}
Ejemplo n.º 3
0
void REPLICATESPU_APIENTRY replicatespu_End( void )
{
	GET_THREAD(thread);
	CRPackBuffer *buf = &thread->BeginEndBuffer;

	if ( thread->server.conn->Barf &&
		(thread->BeginEndMode == GL_LINES
		|| thread->BeginEndMode == GL_TRIANGLES
		|| thread->BeginEndMode == GL_QUADS
		|| thread->BeginEndMode == GL_POLYGON ) )
	{
		CRASSERT(buf->pack);

		crPackReleaseBuffer( thread->packer );
		crPackSetBuffer( thread->packer, &thread->normBuffer );
		if ( !crPackCanHoldBuffer( buf ) )
			replicatespuFlush( (void *) thread );

		crPackAppendBuffer( buf );
		crNetFree( thread->server.conn, buf->pack );
		buf->pack = NULL;
	}

	if (thread->currentContext->displayListMode != GL_FALSE) {
		crDLMCompileEnd();
	}
	if (replicate_spu.swap)
	{
		crPackEndSWAP();
	}
	else
	{
		crPackEnd();
	}
}
Ejemplo n.º 4
0
/* 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;
}
Ejemplo n.º 5
0
/*
 * Allocate a new ThreadInfo structure, setup a connection to the
 * server, allocate/init a packer context, bind this ThreadInfo to
 * the calling thread with crSetTSD().
 * We'll always call this function at least once even if we're not
 * using threads.
 */
ThreadInfo *replicatespuNewThread( CRthread id )
{
	ThreadInfo *thread;

#ifdef CHROMIUM_THREADSAFE_notyet
	crLockMutex(&_ReplicateMutex);
#else
	CRASSERT(replicate_spu.numThreads == 0);
#endif

	CRASSERT(replicate_spu.numThreads < MAX_THREADS);
	thread = &(replicate_spu.thread[replicate_spu.numThreads]);

	thread->id = id;
	thread->currentContext = NULL;

	/* connect to the server */
	thread->server.name = crStrdup( replicate_spu.name );
	thread->server.buffer_size = replicate_spu.buffer_size;
	if (replicate_spu.numThreads == 0) {
		replicatespuConnectToServer( &(thread->server) );
		CRASSERT(thread->server.conn);
		replicate_spu.swap = thread->server.conn->swap;
	}
	else {
		/* a new pthread */
		replicatespuFlushAll( &(replicate_spu.thread[0]) );
		crNetNewClient( replicate_spu.thread[0].server.conn, &(thread->server));
		CRASSERT(thread->server.conn);
	}

	/* packer setup */
	CRASSERT(thread->packer == NULL);
	thread->packer = crPackNewContext( replicate_spu.swap );
	CRASSERT(thread->packer);
	crPackInitBuffer( &(thread->buffer), crNetAlloc(thread->server.conn),
				thread->server.conn->buffer_size, thread->server.conn->mtu );
	thread->buffer.canBarf = thread->server.conn->Barf ? GL_TRUE : GL_FALSE;
	crPackSetBuffer( thread->packer, &thread->buffer );
	crPackFlushFunc( thread->packer, replicatespuFlush );
	crPackFlushArg( thread->packer, (void *) thread );
	crPackSendHugeFunc( thread->packer, replicatespuHuge );
	crPackSetContext( thread->packer );

#ifdef CHROMIUM_THREADSAFE_notyet
	crSetTSD(&_ReplicateTSD, thread);
#endif

	replicate_spu.numThreads++;

#ifdef CHROMIUM_THREADSAFE_notyet
	crUnlockMutex(&_ReplicateMutex);
#endif
	return thread;
}
Ejemplo n.º 6
0
/*
 * This is called from either replicatespuFlushAll or the packer library
 * whenever we need to send a data buffer to the servers.
 */
void replicatespuFlush(void *arg )
{
	ThreadInfo *thread = (ThreadInfo *) arg;
	unsigned int len;
	CRMessageOpcodes *hdr;
	CRPackBuffer *buf;
	unsigned int i;

	/* we should _always_ pass a valid <arg> value */
	CRASSERT(thread);
	buf = &(thread->buffer);
	CRASSERT(buf);
	CRASSERT(buf->pack);

	crPackReleaseBuffer( thread->packer );

	if ( buf->opcode_current == buf->opcode_start ) {
		/* XXX these calls seem to help, but might be appropriate */
		crPackSetBuffer( thread->packer, buf );
		crPackResetPointers(thread->packer);
		return;
	}

	hdr = __prependHeader( buf, &len, 0 );

	/* Now send it to all our replicants */
	for (i = 1; i < CR_MAX_REPLICANTS; i++) 
	{
		if (IS_CONNECTED(replicate_spu.rserver[i].conn))
		{
			 crNetSend( replicate_spu.rserver[i].conn, NULL, hdr, len );
		}
	}

	/* The network may have found a new mtu */
	buf->mtu = thread->server.conn->mtu;

	crPackSetBuffer( thread->packer, buf );

	crPackResetPointers(thread->packer);
}
Ejemplo n.º 7
0
/**
 * Flush buffered commands, sending them to just one server
 */
void
replicatespuFlushOne(ThreadInfo *thread, int server)
{
	unsigned int len;
	CRMessageOpcodes *hdr;
	CRPackBuffer *buf;
	CRConnection *conn;

	CRASSERT(server >= 0);
	CRASSERT(server < CR_MAX_REPLICANTS);
	CRASSERT(thread);
	buf = &(thread->buffer);
	CRASSERT(buf);
	CRASSERT(buf->pack);

	crPackReleaseBuffer( thread->packer );

	if ( buf->opcode_current == buf->opcode_start ) {
		/* XXX these calls seem to help, but might be appropriate */
		crPackSetBuffer( thread->packer, buf );
		crPackResetPointers(thread->packer);
		return;
	}

	hdr = __prependHeader( buf, &len, 0 );

	conn = replicate_spu.rserver[server].conn;
	CRASSERT(conn);

	if (conn->type != CR_NO_CONNECTION) {
		crNetSend( conn, NULL, hdr, len );
	}

	/* The network may have found a new mtu */
	buf->mtu = thread->server.conn->mtu;

	crPackSetBuffer( thread->packer, buf );

	crPackResetPointers(thread->packer);
}
Ejemplo n.º 8
0
static void DoVertex( void )
{
	GET_THREAD(thread);
	CRPackBuffer *buf = &thread->BeginEndBuffer;
	CRPackBuffer *gbuf = &thread->normBuffer;
	int num_data;
	int num_opcode;

	/*crDebug( "really doing Vertex" );*/
	crPackReleaseBuffer( thread->packer );
	num_data = buf->data_current - buf->data_start;
	num_opcode = buf->opcode_start - buf->opcode_current;
	crPackSetBuffer( thread->packer, gbuf );
	if ( !crPackCanHoldBuffer( buf ) )
		/* doesn't hold, first flush gbuf*/
		replicatespuFlush( (void *) thread );

	crPackAppendBuffer( buf );
	crPackReleaseBuffer( thread->packer );
	crPackSetBuffer( thread->packer, buf );
	crPackResetPointers(thread->packer);
}
Ejemplo n.º 9
0
void PACKSPU_APIENTRY packspu_VBoxPackSetInjectThread(void)
{
    crLockMutex(&_PackMutex);
    {
        int i;
        GET_THREAD(thread);
        CRASSERT(!thread);
        CRASSERT((pack_spu.numThreads>0) && (pack_spu.numThreads<MAX_THREADS));

        for (i=0; i<MAX_THREADS; ++i)
        {
            if (!pack_spu.thread[i].inUse)
            {
                thread = &pack_spu.thread[i];
                break;
            }
        }
        CRASSERT(thread);

        thread->inUse = GL_TRUE;
        thread->id = crThreadID();
        thread->currentContext = NULL;
        thread->bInjectThread = GL_TRUE;

        thread->netServer.name = crStrdup(pack_spu.name);
        thread->netServer.buffer_size = 64 * 1024;

        crNetNewClient(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn, &(thread->netServer));
        CRASSERT(thread->netServer.conn);

        CRASSERT(thread->packer == NULL);
        thread->packer = crPackNewContext( pack_spu.swap );
        CRASSERT(thread->packer);
        crPackInitBuffer(&(thread->buffer), crNetAlloc(thread->netServer.conn),
                         thread->netServer.conn->buffer_size, thread->netServer.conn->mtu);
        thread->buffer.canBarf = thread->netServer.conn->Barf ? GL_TRUE : GL_FALSE;

        crPackSetBuffer( thread->packer, &thread->buffer );
        crPackFlushFunc( thread->packer, packspuFlush );
        crPackFlushArg( thread->packer, (void *) thread );
        crPackSendHugeFunc( thread->packer, packspuHuge );
        crPackSetContext( thread->packer );

        crSetTSD(&_PackTSD, thread);

        pack_spu.numThreads++;
    }
    crUnlockMutex(&_PackMutex);
}
Ejemplo n.º 10
0
/*
 * Get an empty packing buffer from the buffer pool, or allocate a new one.
 * Then tell the packer to use it.
 */
void hiddenlineProvidePackBuffer(void)
{
	void *buf;
	GET_CONTEXT(context);

	CRASSERT(context);

	buf = crBufferPoolPop( context->bufpool, hiddenline_spu.buffer_size );
	if (!buf)
	{
		buf = crAlloc( hiddenline_spu.buffer_size );
	}
	crPackInitBuffer( &(context->pack_buffer), buf, hiddenline_spu.buffer_size, hiddenline_spu.buffer_size );
	crPackSetBuffer( context->packer, &(context->pack_buffer) );
}
Ejemplo n.º 11
0
/**
 * Examine the server tile boundaries to compute the overall max
 * viewport dims.  Then send those dims to the servers.
 *
 * XXX \todo This isn't used!?!
 */
void
tilesortspuComputeMaxViewport(WindowInfo *winInfo)
{
	ThreadInfo *thread0 = &(tilesort_spu.thread[0]);
	GLint totalDims[2];
	int i;


	/* release geometry buffer, if it's bound */
	crPackReleaseBuffer( thread0->packer );

	/*
	 * It's hard to say what the max viewport size should be.
	 * We've changed this computation a few times now.
	 * For now, we set it to twice the mural size, or at least 4K.
	 * One problem is that the mural size can change dynamically...
	 */
	totalDims[0] = 2 * winInfo->muralWidth;
	totalDims[1] = 2 * winInfo->muralHeight;
	if (totalDims[0] < 4096)
		totalDims[0] = 4096;
	if (totalDims[1] < 4096)
		totalDims[1] = 4096;

	tilesort_spu.limits.maxViewportDims[0] = totalDims[0];
	tilesort_spu.limits.maxViewportDims[1] = totalDims[1];

	/* 
	 * Once we've computed the maximum viewport size, we send
	 * a message to each server with its new viewport parameters.
	 */
	for (i = 0; i < tilesort_spu.num_servers; i++)
	{
		crPackSetBuffer( thread0->packer, &(thread0->buffer[i]) );

		if (tilesort_spu.swap)
			crPackChromiumParametervCRSWAP(GL_SET_MAX_VIEWPORT_CR, GL_INT, 2, totalDims);
		else
			crPackChromiumParametervCR(GL_SET_MAX_VIEWPORT_CR, GL_INT, 2, totalDims);

		/* release server buffer */
		crPackReleaseBuffer( thread0->packer );

		/* Flush buffer (send to server) */
		tilesortspuSendServerBuffer( i );
	}
}
Ejemplo n.º 12
0
void REPLICATESPU_APIENTRY replicatespu_Begin( GLenum mode )
{
	GET_THREAD(thread);
	CRPackBuffer *buf = &thread->BeginEndBuffer;

	CRASSERT( mode >= GL_POINTS && mode <= GL_POLYGON );

	if (thread->currentContext->displayListMode != GL_FALSE) {
		crDLMCompileBegin(mode);
	}
	if (replicate_spu.swap)
	{
		crPackBeginSWAP( mode );
	}
	else
	{
		crPackBegin( mode );
	}

	if ( thread->server.conn->Barf ) {
		thread->BeginEndMode = mode;
		thread->BeginEndState = -1;
		if ( mode == GL_LINES || mode == GL_TRIANGLES || mode == GL_QUADS || mode == GL_POLYGON )
		{
			CRASSERT(!buf->pack);

			crPackReleaseBuffer( thread->packer );
			buf->pack = crNetAlloc( thread->server.conn );
			crPackInitBuffer( buf, buf->pack, thread->server.conn->buffer_size, thread->server.conn->mtu );
			buf->holds_BeginEnd = 1;
			buf->in_BeginEnd = 1;
			crPackSetBuffer( thread->packer, buf );

			thread->BeginEndState = 0;
		}
	}
}
Ejemplo n.º 13
0
GLint PACKSPU_APIENTRY packspu_VBoxPackSetInjectThread(struct VBOXUHGSMI *pHgsmi)
{
    GLint con = 0;
    int i;
    GET_THREAD(thread);
    CRASSERT(!thread);
    crLockMutex(&_PackMutex);
    {
        CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI() || (pack_spu.numThreads>0));
        CRASSERT(pack_spu.numThreads<MAX_THREADS);
        for (i=0; i<MAX_THREADS; ++i)
        {
            if (!pack_spu.thread[i].inUse)
            {
                thread = &pack_spu.thread[i];
                break;
            }
        }
        CRASSERT(thread);

        thread->inUse = GL_TRUE;
        if (!CRPACKSPU_IS_WDDM_CRHGSMI())
            thread->id = crThreadID();
        else
            thread->id = THREAD_OFFSET_MAGIC + i;
        thread->currentContext = NULL;
        thread->bInjectThread = GL_TRUE;

        thread->netServer.name = crStrdup(pack_spu.name);
        thread->netServer.buffer_size = 64 * 1024;

        packspuConnectToServer(&(thread->netServer)
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
                , pHgsmi
#endif
        );
        CRASSERT(thread->netServer.conn);

        CRASSERT(thread->packer == NULL);
        thread->packer = crPackNewContext( pack_spu.swap );
        CRASSERT(thread->packer);
        crPackInitBuffer(&(thread->buffer), crNetAlloc(thread->netServer.conn),
                         thread->netServer.conn->buffer_size, thread->netServer.conn->mtu);
        thread->buffer.canBarf = thread->netServer.conn->Barf ? GL_TRUE : GL_FALSE;

        crPackSetBuffer( thread->packer, &thread->buffer );
        crPackFlushFunc( thread->packer, packspuFlush );
        crPackFlushArg( thread->packer, (void *) thread );
        crPackSendHugeFunc( thread->packer, packspuHuge );
        crPackSetContext( thread->packer );

        crSetTSD(&_PackTSD, thread);

        pack_spu.numThreads++;
    }
    crUnlockMutex(&_PackMutex);

    if (CRPACKSPU_IS_WDDM_CRHGSMI())
    {
        CRASSERT(thread->id - THREAD_OFFSET_MAGIC < RT_ELEMENTS(pack_spu.thread)
                && GET_THREAD_VAL_ID(thread->id) == thread);
        con = thread->id;
    }
    return con;
}
Ejemplo n.º 14
0
/**
 * Implementation of glClean for tilesorter
 * \param mask
 */
void TILESORTSPU_APIENTRY tilesortspu_Clear( GLbitfield mask )
{
	GET_THREAD(thread);
	GLenum dlMode = thread->currentContext->displayListMode;
	WindowInfo *winInfo = thread->currentContext->currentWindow;

	/* This is a good place to check for new tiling geometry when
	 * the DMX window is changed.
	 */
#ifdef USE_DMX
	if (tilesort_spu.trackWindowPosition && !dlMode) {
		if (winInfo->isDMXWindow && winInfo->xwin) {
			if (tilesortspuUpdateWindowInfo(winInfo)) {
				tilesortspuGetNewTiling(winInfo);
			}
		}
	}

	if (winInfo->newBackendWindows) {
		tilesortspuGetNewTiling(winInfo);
	}
#endif

	if (dlMode != GL_FALSE) {
		/* just creating and/or compiling display lists */
		if (tilesort_spu.lazySendDLists)
			crDLMCompileClear(mask);
		else if (tilesort_spu.swap)
			crPackClearSWAP(mask);
		else
			crPackClear(mask);
		return;
	}

	if (winInfo->passiveStereo) {
		/* only send Clear to left/right servers */
		int i;

		tilesortspuFlush( thread );

		crPackReleaseBuffer( thread->packer );

		/* Send glClear command to those servers designated as left/right
		 * which match the current glDrawBuffer setting (stereo).
		 */
		for (i = 0; i < tilesort_spu.num_servers; i++)
		{
			const ServerWindowInfo *servWinInfo = winInfo->server + i;

			if (servWinInfo->eyeFlags & thread->currentContext->stereoDestFlags) {
				crPackSetBuffer( thread->packer, &(thread->buffer[i]) );

				if (tilesort_spu.swap)
					crPackClearSWAP(mask);
				else
					crPackClear(mask);

				crPackReleaseBuffer( thread->packer );

				tilesortspuSendServerBuffer( i );
			}
		}

		/* Restore the default pack buffer */
		crPackSetBuffer( thread->packer, &(thread->geometry_buffer) );
	}
	else {
		/* not doing stereo, truly broadcast glClear */
		tilesortspuFlush( thread );
		if (tilesort_spu.swap)
			crPackClearSWAP( mask );
		else
			crPackClear( mask );
		tilesortspuBroadcastGeom(GL_TRUE);
	}
}
Ejemplo n.º 15
0
/*
 * Allocate a new ThreadInfo structure, setup a connection to the
 * server, allocate/init a packer context, bind this ThreadInfo to
 * the calling thread with crSetTSD().
 * We'll always call this function at least once even if we're not
 * using threads.
 */
ThreadInfo *packspuNewThread( unsigned long id )
{
    ThreadInfo *thread=NULL;
    int i;

#ifdef CHROMIUM_THREADSAFE
    crLockMutex(&_PackMutex);
#else
    CRASSERT(pack_spu.numThreads == 0);
#endif

    CRASSERT(pack_spu.numThreads < MAX_THREADS);
    for (i=0; i<MAX_THREADS; ++i)
    {
        if (!pack_spu.thread[i].inUse)
        {
            thread = &pack_spu.thread[i];
            break;
        }
    }
    CRASSERT(thread);

    thread->inUse = GL_TRUE;
    thread->id = id;
    thread->currentContext = NULL;
    thread->bInjectThread = GL_FALSE;

    /* connect to the server */
    thread->netServer.name = crStrdup( pack_spu.name );
    thread->netServer.buffer_size = pack_spu.buffer_size;
    if (pack_spu.numThreads == 0) {
        packspuConnectToServer( &(thread->netServer) );
        if (!thread->netServer.conn) {
            return NULL;
        }
        pack_spu.swap = thread->netServer.conn->swap;
    }
    else {
        /* a new pthread */
        crNetNewClient(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn, &(thread->netServer));
        CRASSERT(thread->netServer.conn);
    }

    /* packer setup */
    CRASSERT(thread->packer == NULL);
    thread->packer = crPackNewContext( pack_spu.swap );
    CRASSERT(thread->packer);
    crPackInitBuffer( &(thread->buffer), crNetAlloc(thread->netServer.conn),
                thread->netServer.conn->buffer_size, thread->netServer.conn->mtu );
    thread->buffer.canBarf = thread->netServer.conn->Barf ? GL_TRUE : GL_FALSE;
    crPackSetBuffer( thread->packer, &thread->buffer );
    crPackFlushFunc( thread->packer, packspuFlush );
    crPackFlushArg( thread->packer, (void *) thread );
    crPackSendHugeFunc( thread->packer, packspuHuge );
    crPackSetContext( thread->packer );

#ifdef CHROMIUM_THREADSAFE
    crSetTSD(&_PackTSD, thread);
#endif

    pack_spu.numThreads++;

#ifdef CHROMIUM_THREADSAFE
    crUnlockMutex(&_PackMutex);
#endif
    return thread;
}
Ejemplo n.º 16
0
/*
 * This is called from either the Pack SPU and the packer library whenever
 * we need to send a data buffer to the server.
 */
void packspuFlush(void *arg )
{
    ThreadInfo *thread = (ThreadInfo *) arg;
    ContextInfo *ctx;
    unsigned int len;
    CRMessageOpcodes *hdr;
    CRPackBuffer *buf;

    /* we should _always_ pass a valid <arg> value */
    CRASSERT(thread && thread->inUse);
#ifdef CHROMIUM_THREADSAFE
    CR_LOCK_PACKER_CONTEXT(thread->packer);
#endif
    ctx = thread->currentContext;
    buf = &(thread->buffer);
    CRASSERT(buf);

    if (ctx && ctx->fCheckZerroVertAttr)
        crStateCurrentRecoverNew(ctx->clientState, &thread->packer->current);

    /* We're done packing into the current buffer, unbind it */
    crPackReleaseBuffer( thread->packer );

    /*
    printf("%s thread=%p thread->id = %d thread->pc=%p t2->id=%d t2->pc=%p packbuf=%p packbuf=%p\n",
           __FUNCTION__, (void*) thread, (int) thread->id, thread->packer,
           (int) t2->id, t2->packer,
           buf->pack, thread->packer->buffer.pack);
    */

    if ( buf->opcode_current == buf->opcode_start ) {
           /*
           printf("%s early return\n", __FUNCTION__);
           */
           /* XXX these calls seem to help, but might be appropriate */
           crPackSetBuffer( thread->packer, buf );
           crPackResetPointers(thread->packer);
#ifdef CHROMIUM_THREADSAFE
           CR_UNLOCK_PACKER_CONTEXT(thread->packer);
#endif
           return;
    }

    hdr = __prependHeader( buf, &len, 0 );

    CRASSERT( thread->netServer.conn );

    if ( buf->holds_BeginEnd )
    {
        /*crDebug("crNetBarf %d, (%d)", len, buf->size);*/
        crNetBarf( thread->netServer.conn, &(buf->pack), hdr, len );
    }
    else
    {
        /*crDebug("crNetSend %d, (%d)", len, buf->size);*/
        crNetSend( thread->netServer.conn, &(buf->pack), hdr, len );
    }

    buf->pack = crNetAlloc( thread->netServer.conn );

    /* The network may have found a new mtu */
    buf->mtu = thread->netServer.conn->mtu;

    crPackSetBuffer( thread->packer, buf );

    crPackResetPointers(thread->packer);

#ifdef CHROMIUM_THREADSAFE
    CR_UNLOCK_PACKER_CONTEXT(thread->packer);
#endif
}