/* 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_environment.h" #include "cr_string.h" #include "cr_dll.h" #include "cr_error.h" #include "cr_spu.h" #include #include #include #include #ifdef WINDOWS #ifdef VBOX_WDDM_WOW64 #define DLL_SUFFIX "-x86.dll" #else #define DLL_SUFFIX ".dll" #endif #define DLL_PREFIX "VBoxOGL" #define snprintf _snprintf #elif defined(DARWIN) #define DLL_SUFFIX ".dylib" #define DLL_PREFIX "VBoxOGL" /* #define DLL_SUFFIX ".bundle" #define DLL_PREFIX "" */ #else #ifdef AIX #define DLL_SUFFIX ".o" #define DLL_PREFIX "VBoxOGL" #else #define DLL_SUFFIX ".so" #define DLL_PREFIX "VBoxOGL" #endif #endif extern void __buildDispatch( SPU *spu ); static char *__findDLL( char *name, char *dir ) { static char path[8092]; if (!dir) { #if defined(DARWIN) char szSharedLibPath[8092]; int rc = RTPathAppPrivateArch (szSharedLibPath, sizeof(szSharedLibPath)); if (RT_SUCCESS(rc)) sprintf ( path, "%s/%s%sspu%s", szSharedLibPath, DLL_PREFIX, name, DLL_SUFFIX ); else #endif /* DARWIN */ #ifdef VBOX snprintf ( path, sizeof(path), "%s%sspu%s", DLL_PREFIX, name, DLL_SUFFIX ); #else sprintf ( path, "%s%sspu%s", DLL_PREFIX, name, DLL_SUFFIX ); #endif } else { #ifdef VBOX snprintf ( path, sizeof(path), "%s/%s%sspu%s", dir, DLL_PREFIX, name, DLL_SUFFIX ); #else sprintf ( path, "%s/%s%sspu%s", dir, DLL_PREFIX, name, DLL_SUFFIX ); #endif } return path; } /** * Load a single SPU from disk and initialize it. Is there any reason * to export this from the SPU loader library? */ SPU * crSPULoad( SPU *child, int id, char *name, char *dir, void *server ) { SPU *the_spu; char *path; CRASSERT( name != NULL ); the_spu = (SPU*)crAlloc( sizeof( *the_spu ) ); /* ensure all fields are initially zero, * NOTE: what actually MUST be zero at this point is the_spu->superSPU, otherwise * crSPUUnloadChain in the failure branches below will misbehave */ crMemset(the_spu, 0, sizeof (*the_spu)); the_spu->id = id; the_spu->privatePtr = NULL; path = __findDLL( name, dir ); the_spu->dll = crDLLOpen( path, 0/*resolveGlobal*/ ); #if defined(DEBUG_misha) && defined(RT_OS_WINDOWS) crDbgCmdSymLoadPrint(path, the_spu->dll->hinstLib); #endif the_spu->entry_point = (SPULoadFunction) crDLLGetNoError( the_spu->dll, SPU_ENTRY_POINT_NAME ); if (!the_spu->entry_point) { crError( "Couldn't load the SPU entry point \"%s\" from SPU \"%s\"!", SPU_ENTRY_POINT_NAME, name ); crSPUUnloadChain(the_spu); return NULL; } /* This basically calls the SPU's SPULoad() function */ if (!the_spu->entry_point( &(the_spu->name), &(the_spu->super_name), &(the_spu->init), &(the_spu->self), &(the_spu->cleanup), &(the_spu->options), &(the_spu->spu_flags)) ) { crError( "I found the SPU \"%s\", but loading it failed!", name ); crSPUUnloadChain(the_spu); return NULL; } #ifdef IN_GUEST if (crStrcmp(the_spu->name,"error")) { /* the default super/base class for an SPU is the error SPU */ if (the_spu->super_name == NULL) { the_spu->super_name = "error"; } the_spu->superSPU = crSPULoad( child, id, the_spu->super_name, dir, server ); } #else if (crStrcmp(the_spu->name,"hosterror")) { /* the default super/base class for an SPU is the error SPU */ if (the_spu->super_name == NULL) { the_spu->super_name = "hosterror"; } the_spu->superSPU = crSPULoad( child, id, the_spu->super_name, dir, server ); } #endif else { the_spu->superSPU = NULL; } crDebug("Initializing %s SPU", name); the_spu->function_table = the_spu->init( id, child, the_spu, 0, 1 ); if (!the_spu->function_table) { crDebug("Failed to init %s SPU", name); crSPUUnloadChain(the_spu); return NULL; } __buildDispatch( the_spu ); /*crDebug( "initializing dispatch table %p (for SPU %s)", (void*)&(the_spu->dispatch_table), name );*/ crSPUInitDispatchTable( &(the_spu->dispatch_table) ); /*crDebug( "Done initializing the dispatch table for SPU %s, calling the self function", name );*/ the_spu->dispatch_table.server = server; the_spu->self( &(the_spu->dispatch_table) ); /*crDebug( "Done with the self function" );*/ return the_spu; } /** * Load the entire chain of SPUs and initialize all of them. * This function returns the first one in the chain. */ SPU * crSPULoadChain( int count, int *ids, char **names, char *dir, void *server ) { int i; SPU *child_spu = NULL; CRASSERT( count > 0 ); for (i = count-1 ; i >= 0 ; i--) { int spu_id = ids[i]; char *spu_name = names[i]; SPU *the_spu, *temp; /* This call passes the previous version of spu, which is the SPU's * "child" in this chain. */ the_spu = crSPULoad( child_spu, spu_id, spu_name, dir, server ); if (!the_spu) { return NULL; } if (child_spu != NULL) { /* keep track of this so that people can pass functions through but * still get updated when API's change on the fly. */ for (temp = the_spu ; temp ; temp = temp->superSPU ) { struct _copy_list_node *node = (struct _copy_list_node *) crAlloc( sizeof( *node ) ); node->copy = &(temp->dispatch_table); node->next = child_spu->dispatch_table.copyList; child_spu->dispatch_table.copyList = node; } } child_spu = the_spu; } return child_spu; } #if 00 /* XXXX experimental code - not used at this time */ /** * Like crSPUChangeInterface(), but don't loop over all functions in * the table to search for 'old_func'. */ void crSPUChangeFunction(SPUDispatchTable *table, unsigned int funcOffset, void *newFunc) { SPUGenericFunction *f = (SPUGenericFunction *) table + funcOffset; struct _copy_list_node *temp; CRASSERT(funcOffset < sizeof(*table) / sizeof(SPUGenericFunction)); printf("%s\n", __FUNCTION__); if (table->mark == 1) return; table->mark = 1; *f = newFunc; /* update all copies of this table */ #if 1 for (temp = table->copyList ; temp ; temp = temp->next) { crSPUChangeFunction( temp->copy, funcOffset, newFunc ); } #endif if (table->copy_of != NULL) { crSPUChangeFunction( table->copy_of, funcOffset, newFunc ); } #if 0 for (temp = table->copyList ; temp ; temp = temp->next) { crSPUChangeFunction( temp->copy, funcOffset, newFunc ); } #endif table->mark = 0; } #endif /** * Call the cleanup() function for each SPU in a chain, close the SPU * DLLs and free the SPU objects. * \param headSPU pointer to the first SPU in the chain */ void crSPUUnloadChain(SPU *headSPU) { SPU *the_spu = headSPU, *next_spu; while (the_spu) { crDebug("Cleaning up SPU %s", the_spu->name); if (the_spu->cleanup) the_spu->cleanup(); next_spu = the_spu->superSPU; crDLLClose(the_spu->dll); crFree(the_spu); the_spu = next_spu; } }