/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmcr.c,v 1.13 2005/08/09 14:43:43 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.13 $
 * $Log: rmcr.c,v $
 * Revision 1.13  2005/08/09 14:43:43  wes
 * Added error checking code to confirm presence of CR-specific structure
 * in the RMpipe before performing CR-specific operations.
 *
 * Revision 1.12  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.11  2005/05/16 01:00:51  wes
 * Minor code rearrangement to better accommodate RM_X and RM_CR compile
 * definitions (both might be used at once).
 *
 * Revision 1.10  2005/02/19 16:22:50  wes
 * Distro sync and consolidation.
 *
 * Revision 1.9  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.8  2004/01/16 17:18:34  wes
 * Updated copyright line. Added more error testing to the Chromium-specific
 * RMpipe initialization sequence to help diagnose Chromium configuration
 * problems (there were a lot of questions related to CR config during
 * early field tests).
 *
 * Revision 1.7  2003/08/14 13:47:22  wes
 * Enclosed some debugging printf's inside conditional compilation directives.
 *
 * Revision 1.6  2003/07/27 15:37:37  wes
 * Fixed minor problems with in-line documentation.
 *
 * Revision 1.5  2003/04/05 13:58:52  wes
 * Remove dead code.
 *
 * Revision 1.4  2003/03/16 21:56:16  wes
 * Documentation updates.
 *
 * Revision 1.3  2003/02/18 15:40:40  wes
 * Added RMpipe wrapper functions for CR barrier creation and execution.
 *
 * Revision 1.2  2003/02/14 00:16:39  wes
 * Use of new CR-specific struct in the RMpipe, rather than a static struct.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.7  2003/01/27 05:04:42  wes
 * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR
 * platforms w/o too much disruption to existing apps.
 *
 * Revision 1.6  2003/01/20 05:39:49  wes
 * Rewrote texture state handling code.
 *
 * Revision 1.5  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.4  2003/01/15 16:11:24  wes
 * Migrate setting of parms asking for shared display lists and texture objects
 * into the CR .conf files, rather than setting during RMpipe intialization. This
 * policy may change in the future.
 *
 * Revision 1.3  2003/01/14 05:28:57  wes
 * Added calls to CR extensions that enable shared display lists,
 * and texture object IDs.
 *
 * Revision 1.2  2003/01/09 16:58:59  wes
 * Removed c++'isms that lingered from a previous c++ version. These c++'isms
 * work OK in C sources in gcc 3.2, but not in earlier versions.
 *
 * Revision 1.1  2002/12/31 00:51:51  wes
 * Initial entry.
 *
 */


#include <rm/rm.h>
#include "rmprivat.h"

#ifdef RM_CR
#include <rm/rmcr.h>
#define CR_REASONABLE_MURALSIZE(a) (((a) > 1) && ((a) < 32768))
#endif


/*
 * ----------------------------------------------------
 * @Name rmPipeBarrierCreateCR
 @pstart
 RMenum rmPipeBarrierCreateCR (RMpipe *toModify, int barrierNumber)
 @pend

 @astart
 RMpipe *toModify - a handle to an RMpipe to be modified.
 int barrierNumber - an integer value specifying a barrier number.
 @aend

 @dstart

 (For use with Chromium only)
 Use this routine to create a named barrier. The barrier is executed by
 calling rmPipeBarrierExecCR(). Chromium barriers are intended to be used to
 synchronize execution of OpenGL streams on crservers.

 Chromium barriers, when created, require an integer value indicating the
 number of streams that will be participating in the barrier. This integer
 value is provided automatically by this routine. Use the routine
 rmPipeSetCommSize(), prior to calling rmPipeCreateBarrierCR(), to set
 the number of crservers that will be participating in execution of the
 barrier.

 Chromium barriers, when executed, synchronize execution of OpenGL code
 in the crservers. They do not block the application code.

 This routine returns RM_CHILL upon success. This routine will return
 RM_WHACKED if the input RMpipe is NULL, or if the size of the communication
 collective is zero (use rmPipeSetCommSize to set the size of the collective).
 This routine does not check for creation of duplicate barriers.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeBarrierCreateCR (RMpipe *toModify,
		       int barrierNumber)
{
    if (RM_ASSERT(toModify, "rmPipeBarrierCreateCR() error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);

    if (toModify->globalNPE == 0)
    {
	rmError("rmPipeBarrierCreateCR() error - the size of the communination collective is zero. Use rmPipeSetCommSize() to set a non-zero collective size prior to calling rmPipeBarrierCreateCR.");
	return RM_WHACKED;
    }

#ifdef RM_CR
    if (toModify->crStuff != NULL)
	toModify->crStuff->glBarrierCreateCR(barrierNumber, toModify->globalNPE);
    else
	rmError("rmPipeBarrierCreateCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
#else
    {
	barrierNumber++; 	/* foil compiler warning */
	rmWarning("rmPipeBarrierCreateCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
    }
#endif
    return RM_CHILL;
}

/*
 * ----------------------------------------------------
 * @Name rmPipeBarrierExecCR
 @pstart
 RMenum rmPipeBarrierExecCR (RMpipe *toUse, int barrierNumber)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe to be used.
 int barrierNumber - an integer value specifying a barrier number.
 @aend

 @dstart

 (For use with Chromium only)
 Use this routine to issue a "barrier execute" command to Chromium. 
 Chromium barriers are intended to be used to synchronize execution of
 OpenGL streams on crservers.

 Chromium barriers, when created, require an integer value indicating the
 number of streams that will be participating in the barrier. This integer
 value is provided automatically by this routine. Use the routine
 rmPipeSetCommSize(), prior to calling rmPipeBarrierCreateCR(), to set
 the number of crservers that will be participating in execution of the
 barrier.

 Chromium barriers, when executed, synchronize execution of OpenGL code
 in the crservers. They do not block the application code.

 This routine returns RM_CHILL upon success, or RM_WHACKED if the input
 RMpipe is NULL. This routine does not check for creation of duplicate
 barriers.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeBarrierExecCR (RMpipe *toUse,
		     int barrierNumber)
{
    if (RM_ASSERT(toUse, "rmPipeBarrierExecCR() error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);

#ifdef RM_CR
    if (toUse->crStuff != NULL)
	toUse->crStuff->glBarrierExecCR(barrierNumber);
    else
	rmError("rmPipeBarrierExecCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
	
#else
    barrierNumber++; 		/* foil compiler warning */
    rmWarning("rmPipeBarrierExecCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif
    return RM_CHILL;
}


RMenum
rmPipeSwapBuffersCR(const RMpipe *p)
{
#ifdef RM_CR
    int ival = (p->myRank == 0); /* only PE 0 does "real" swapbuffers */

#if (DEBUG_LEVEL & DEBUG_TRACE)
    fprintf(stderr," PE %d entering rmPipeSwapBuffersCR \n", p->myRank);
    fflush(stderr);
#endif

    if (p->crStuff != NULL)
	p->crStuff->glBarrierExecCR( RM_CR_PRE_SWAPBUFFERS_BARRIER );
    else
    {
	rmError("rmPipeSwapBuffersCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
	return RM_WHACKED;
    }

    if (ival == 1)
	p->crStuff->glSwapBuffersCR( 0, 0);
    else
	p->crStuff->glSwapBuffersCR( 0, CR_SUPPRESS_SWAP_BIT);

    p->crStuff->glBarrierExecCR( RM_CR_POST_SWAPBUFFERS_BARRIER );
#else
    p = NULL; 			/* foil compiler warnings */
    rmWarning("rmPipeSwapBuffersCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif

#if (DEBUG_LEVEL & DEBUG_TRACE)
    fprintf(stderr," PE %d leaving rmPipeSwapBuffersCR \n", p->myRank);
    fflush(stderr);
#endif
  
    return RM_CHILL;
}

RMenum
rmPipeCreateContextCR(RMpipe *pipe)
{
#ifdef RM_CR
    int ivisual = CR_RGB_BIT | CR_DEPTH_BIT | CR_DOUBLE_BIT ;
    int ictx;

    if (pipe->crStuff != NULL)
	ictx = pipe->crStuff->glCreateContextCR(getenv("DISPLAY"), ivisual);
    else
    {
	rmError("rmPipeCreateContextCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
	return RM_WHACKED;
    }

    if (ictx < 0) 
    {
       rmError("rmPipeInitCR(), glCreateContextCR() call failed!\n");
       return RM_WHACKED;
    }
    pipe->contextCR = ictx;
#else
    pipe = NULL; 		/* foil compiler warning */
    rmWarning("rmPipeCreateContextCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif
    return RM_CHILL;
}

RMenum
rmPipeMakeCurrentCR(RMpipe *pipe)
{
    RMenum stat = RM_TRUE;

    /* 5/1/05 - we note there really is no error checking in this routine */
#ifdef RM_CR
    int muralSize[2];
    char buf[1024];

#if (DEBUG_LEVEL & DEBUG_TRACE)
    fprintf(stderr, "rmPipeMakeCurrentCR() - need to add code to honor stereo vs. not before context creation. get rid of viewport setting? \n");
    fflush(stderr);
#endif

    sprintf(buf,"CR_APPLICATION_ID_NUMBER=%d",pipe->myRank);
    putenv(buf);

    if (pipe->crStuff != NULL)
	pipe->crStuff->glMakeCurrentCR(0, pipe->contextCR);
    else
    {
	rmError("rmPipeMakeCurrentCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
	return RM_WHACKED;
    }

    pipe->crStuff->glGetChromiumParametervCR(GL_MURAL_SIZE_CR, 0, GL_INT, 2, muralSize );

    /* deal with the case when there's no mothership */
    /*
     * before Cr 1.8, if there was no mothership, the default muralsize was
     * set to zero, which we could detect and set to 256x256 since that is the
     * default "fake render window" size used by Cr. In Cr 1.8, the behavior
     * appears to have been changed so that the default mural size is 1x1
     * when there is no mothership. Ugh!
     */
    if ((muralSize[0] == 0 || muralSize[0] == 1) &&
	(muralSize[1] == 0 || muralSize[1] == 1))
       muralSize[0] = muralSize[1] = 256;

    fprintf(stderr,"rmPipeInitCR(): PE %d thinks the mural size is %d,%d \n", pipe->myRank, muralSize[0], muralSize[1]);
    fflush(stderr);

    if (CR_REASONABLE_MURALSIZE(muralSize[0]) && CR_REASONABLE_MURALSIZE(muralSize[1]))
	rmPipeSetWindowSize(pipe, muralSize[0], muralSize[1]);
    else
    {
	char buf[512];
	sprintf(buf,"rmPipeInitCR(): PE %d: since the mural size is not reasonable, I'm setting the mural size to (256,256) because those are the values used by CR for a render spu with no mothership. \n", pipe->myRank);
	rmWarning(buf);
	
	rmPipeSetWindowSize(pipe, 256, 256);
    }

    {
      float vp[4] = {0., 0., 1., 1.};

      rmNodeSetSceneViewport(rmRootNode(), vp);
    }


    /* Chromium-specific stuff:
       1. create a barrier that will be called by all PEs prior to swapbuff.
       2. create a barrier to sync up fbclears */

    pipe->myRank = pipe->myRank;
    pipe->globalNPE = pipe->globalNPE;

    pipe->crStuff->glBarrierCreateCR(RM_CR_PRE_SWAPBUFFERS_BARRIER, pipe->globalNPE);
    pipe->crStuff->glBarrierCreateCR(RM_CR_POST_SWAPBUFFERS_BARRIER, pipe->globalNPE);
    pipe->crStuff->glBarrierCreateCR(RM_CR_POST_FBCLEAR_BARRIER, pipe->globalNPE);
    pipe->postFBClearBarrierFunc = private_rmPipePostFBClearBarrierCR;

#else
    pipe = NULL; 			/* foil compiler warning */
    rmWarning("rmPipeMakeCurrentCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif
    

    return(stat);
}

/* private */
void
private_rmPipePostFBClearBarrierCR(const RMpipe *p)
{
#ifdef RM_CR
    if (p->crStuff != NULL)
	p->crStuff->glBarrierExecCR(RM_CR_POST_FBCLEAR_BARRIER);
    else
	rmError("private_rmPipePostFBClearBarrierCR error: the input RMpipe does not appear to be configured for use with Chromium. Please create the RMpipe using the parameter RM_PIPE_CR to rmPipeNew()");
	
#else
    p = NULL; 			/* foil compiler warnings */
    rmWarning("private_rmPipePostFBClearBarrierCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif
}

/* private */
void
private_rmPipeInitCR(RMpipe *p)
{
#ifdef RM_CR
    RMpipeCRvariables *lCR=NULL;
    
    lCR = (RMpipeCRvariables *)malloc(sizeof(RMpipeCRvariables));
    p->crStuff = (void *)lCR;

    rmPipeSetSwapBuffersFunc(p, rmPipeSwapBuffersCR);
    
    
    lCR->glCreateContextCR = (crCreateContextProc)crGetProcAddress("crCreateContext");
    lCR->glMakeCurrentCR = (crMakeCurrentProc)crGetProcAddress("crMakeCurrent");
    lCR->glSwapBuffersCR = (crSwapBuffersProc)crGetProcAddress("crSwapBuffers");
    
    lCR->glChromiumParametervCR = (glChromiumParametervCRProc)GET_PROC("glChromiumParametervCR");
    
    lCR->glGetChromiumParametervCR = (glGetChromiumParametervCRProc) GET_PROC("glGetChromiumParametervCR");
    
    lCR->glBarrierCreateCR = (glBarrierCreateCRProc)GET_PROC("glBarrierCreateCR");
    lCR->glBarrierExecCR = (glBarrierExecCRProc)GET_PROC("glBarrierExecCR");
    
    lCR->glChromiumParametervCR = (glChromiumParametervCRProc) GET_PROC("glChromiumParametervCR");
    lCR->glChromiumParameteriCR = (glChromiumParameteriCRProc) GET_PROC("glChromiumParameteriCR");

    /*
     * check to see if all the GET_PROC's succeeded. We cannot continue if
     * any of them failed.
     */

    if ((lCR->glCreateContextCR == NULL) ||
	(lCR->glMakeCurrentCR == NULL) ||
	(lCR->glSwapBuffersCR == NULL) ||
	(lCR->glChromiumParametervCR == NULL) ||
	(lCR->glGetChromiumParametervCR == NULL) ||
	(lCR->glBarrierCreateCR == NULL) || 
	(lCR->glBarrierExecCR == NULL) ||
	(lCR->glChromiumParametervCR == NULL) || 
	(lCR->glChromiumParameteriCR == NULL))
    {
	rmError("\n\n \
\tDuring intialization of a Chromium-enabled RMpipe, we encountered \n\
\tfatal error - some of the Chromium-specific routines loaded with \n\
\t'GET_PROC' could not be found. This condition most often happens \n\
\twhen you forget to have the symlinks in /usr/local/cr/lib/Linux \n\
\tthat point from libGL.so and libGL.so.1 to libcrfaker.so. The \n\
\tapplication fatally cripped, and will segfault in a short time. \n\n");
    }

#else
    p = NULL;			/* foil compiler warning */
    rmWarning("private_rmPipeInitCR() is effectively disabled. Build OpenRM/RM using the -DRM_CR compile flag to fully enable Chromium constructs.");
#endif
}

/* private */
void
private_rmPipeCloseContextCR (RMpipe *p)
{
    p = NULL; 			/* foil compiler warning */
    rmWarning("rmcr.c:private_rmPipeCloseContextCR() - need to write CR-specific context-closing routines. ");
}

/* EOF */
