diff options
Diffstat (limited to 'src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c')
| -rw-r--r-- | src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c | 2411 |
1 files changed, 2211 insertions, 200 deletions
diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c index f914569e..1a7e360c 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c @@ -12,7 +12,9 @@ #include "cr_string.h" #include "cr_mem.h" #include "cr_hash.h" +#include "cr_vreg.h" #include "cr_environment.h" +#include "cr_pixeldata.h" #include "server_dispatch.h" #include "state/cr_texture.h" #include "render/renderspu.h" @@ -25,6 +27,7 @@ #endif #include <iprt/assert.h> #include <VBox/err.h> +#include <VBox/log.h> #ifdef VBOXCR_LOGFPS #include <iprt/timer.h> @@ -56,6 +59,8 @@ CRServer cr_server; int tearingdown = 0; /* can't be static */ +static DECLCALLBACK(int) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd); + DECLINLINE(int32_t) crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient) { CRClient *pClient = NULL; @@ -118,11 +123,22 @@ static void deleteContextInfoCallback( void *data ) crFree(c); } +static void deleteMuralInfoCallback( void *data ) +{ + CRMuralInfo *m = (CRMuralInfo *) data; + if (m->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID) /* <- do not do term for default mural as it does not contain any info to be freed, + * and renderspu will destroy it up itself*/ + { + crServerMuralTerm(m); + } + crFree(m); +} static void crServerTearDown( void ) { GLint i; CRClientNode *pNode, *pNext; + GLboolean fOldEnableDiff; /* avoid a race condition */ if (tearingdown) @@ -138,6 +154,17 @@ static void crServerTearDown( void ) crFree( cr_server.overlap_intens ); cr_server.overlap_intens = NULL; + /* needed to make sure window dummy mural not get created on mural destruction + * and generally this should be zeroed up */ + cr_server.currentCtxInfo = NULL; + cr_server.currentWindow = -1; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = NULL; + + /* sync our state with renderspu, + * do it before mural & context deletion to avoid deleting currently set murals/contexts*/ + cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + /* Deallocate all semaphores */ crFreeHashtable(cr_server.semaphores, crFree); cr_server.semaphores = NULL; @@ -149,12 +176,28 @@ static void crServerTearDown( void ) /* Free all context info */ crFreeHashtable(cr_server.contextTable, deleteContextInfoCallback); - /* Free context/window creation info */ - crFreeHashtable(cr_server.pWindowCreateInfoTable, crServerCreateInfoDeleteCB); + /* synchronize with reality */ + fOldEnableDiff = crStateEnableDiffOnMakeCurrent(GL_FALSE); + if(cr_server.MainContextInfo.pContext) + crStateMakeCurrent(cr_server.MainContextInfo.pContext); + crStateEnableDiffOnMakeCurrent(fOldEnableDiff); /* Free vertex programs */ crFreeHashtable(cr_server.programTable, crFree); + /* Free murals */ + crFreeHashtable(cr_server.muralTable, deleteMuralInfoCallback); + + CrPMgrTerm(); + + if (CrBltIsInitialized(&cr_server.Blitter)) + { + CrBltTerm(&cr_server.Blitter); + } + + /* Free dummy murals */ + crFreeHashtable(cr_server.dummyMuralTable, deleteMuralInfoCallback); + for (i = 0; i < cr_server.numClients; i++) { if (cr_server.clients[i]) { CRConnection *conn = cr_server.clients[i]->conn; @@ -174,6 +217,11 @@ static void crServerTearDown( void ) } cr_server.pCleanupClient = NULL; + if (crServerRpwIsInitialized(&cr_server.RpwWorker)) + { + crServerRpwTerm(&cr_server.RpwWorker); + } + #if 1 /* disable these two lines if trying to get stack traces with valgrind */ crSPUUnloadChain(cr_server.head_spu); @@ -183,6 +231,10 @@ static void crServerTearDown( void ) crStateDestroy(); crNetTearDown(); + + VBoxVrListClear(&cr_server.RootVr); + + VBoxVrTerm(); } static void crServerClose( unsigned int id ) @@ -226,8 +278,15 @@ void crServerInit(int argc, char *argv[]) { int i; + const char*env; char *mothership = NULL; CRMuralInfo *defaultMural; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return; + } for (i = 1 ; i < argc ; i++) { @@ -294,6 +353,7 @@ crServerInit(int argc, char *argv[]) */ cr_server.muralTable = crAllocHashtable(); defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; crHashtableAdd(cr_server.muralTable, 0, defaultMural); cr_server.programTable = crAllocHashtable(); @@ -311,9 +371,39 @@ crServerInit(int argc, char *argv[]) cr_server.contextTable = crAllocHashtable(); cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo; + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); + crServerInitDispatch(); + crServerInitTmpCtxDispatch(); crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + crUnpackSetReturnPointer( &(cr_server.return_ptr) ); crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) ); @@ -332,6 +422,13 @@ void crVBoxServerTearDown(void) GLboolean crVBoxServerInit(void) { CRMuralInfo *defaultMural; + const char*env; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return GL_FALSE; + } #if DEBUG_FP_EXCEPTIONS { @@ -367,6 +464,7 @@ GLboolean crVBoxServerInit(void) */ cr_server.muralTable = crAllocHashtable(); defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; crHashtableAdd(cr_server.muralTable, 0, defaultMural); cr_server.programTable = crAllocHashtable(); @@ -385,8 +483,27 @@ GLboolean crVBoxServerInit(void) * Default context */ cr_server.contextTable = crAllocHashtable(); -// cr_server.pContextCreateInfoTable = crAllocHashtable(); - cr_server.pWindowCreateInfoTable = crAllocHashtable(); + + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); crServerSetVBoxConfigurationHGCM(); @@ -394,8 +511,17 @@ GLboolean crVBoxServerInit(void) return GL_FALSE; crServerInitDispatch(); + crServerInitTmpCtxDispatch(); crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + /*Check for PBO support*/ if (crStateGetCurrent()->extensions.ARB_pixel_buffer_object) { @@ -405,6 +531,38 @@ GLboolean crVBoxServerInit(void) return GL_TRUE; } +static int32_t crVBoxServerAddClientObj(uint32_t u32ClientID, CRClient **ppNewClient) +{ + CRClient *newClient; + + if (cr_server.numClients>=CR_MAX_CLIENTS) + { + if (ppNewClient) + *ppNewClient = NULL; + return VERR_MAX_THRDS_REACHED; + } + + newClient = (CRClient *) crCalloc(sizeof(CRClient)); + crDebug("crServer: AddClient u32ClientID=%d", u32ClientID); + + newClient->spu_id = 0; + newClient->currentCtxInfo = &cr_server.MainContextInfo; + newClient->currentContextNumber = -1; + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->conn->u32ClientID = u32ClientID; + + cr_server.clients[cr_server.numClients++] = newClient; + + crServerAddToRunQueue(newClient); + + if (ppNewClient) + *ppNewClient = newClient; + + return VINF_SUCCESS; +} + int32_t crVBoxServerAddClient(uint32_t u32ClientID) { CRClient *newClient; @@ -614,6 +772,23 @@ int32_t crVBoxServerClientRead(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t return crVBoxServerInternalClientRead(pClient, pBuffer, pcbBuffer); } +extern DECLEXPORT(int32_t) crVBoxServerClientGetCaps(uint32_t u32ClientID, uint32_t *pu32Caps) +{ + *pu32Caps = cr_server.u32Caps; + return VINF_SUCCESS; +} + +static int32_t crVBoxServerClientObjSetVersion(CRClient *pClient, uint32_t vMajor, uint32_t vMinor) +{ + pClient->conn->vMajor = vMajor; + pClient->conn->vMinor = vMinor; + + if (vMajor != CR_PROTOCOL_VERSION_MAJOR + || vMinor != CR_PROTOCOL_VERSION_MINOR) + return VERR_NOT_SUPPORTED; + return VINF_SUCCESS; +} + int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint32_t vMinor) { CRClient *pClient=NULL; @@ -630,15 +805,14 @@ int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint } if (!pClient) return VERR_INVALID_PARAMETER; - pClient->conn->vMajor = vMajor; - pClient->conn->vMinor = vMinor; + return crVBoxServerClientObjSetVersion(pClient, vMajor, vMinor); +} - if (vMajor != CR_PROTOCOL_VERSION_MAJOR - || vMinor != CR_PROTOCOL_VERSION_MINOR) - { - return VERR_NOT_SUPPORTED; - } - else return VINF_SUCCESS; +static int32_t crVBoxServerClientObjSetPID(CRClient *pClient, uint64_t pid) +{ + pClient->pid = pid; + + return VINF_SUCCESS; } int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid) @@ -657,9 +831,7 @@ int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid) } if (!pClient) return VERR_INVALID_PARAMETER; - pClient->pid = pid; - - return VINF_SUCCESS; + return crVBoxServerClientObjSetPID(pClient, pid); } int @@ -690,13 +862,16 @@ static void crVBoxServerSaveMuralCB(unsigned long key, void *data1, void *data2) rc = SSMR3PutMem(pSSM, &key, sizeof(key)); CRASSERT(rc == VINF_SUCCESS); - rc = SSMR3PutMem(pSSM, pMI, sizeof(*pMI)); + rc = SSMR3PutMem(pSSM, pMI, RT_OFFSETOF(CRMuralInfo, CreateInfo)); CRASSERT(rc == VINF_SUCCESS); if (pMI->pVisibleRects) { rc = SSMR3PutMem(pSSM, pMI->pVisibleRects, 4*sizeof(GLint)*pMI->cVisibleRects); } + + rc = SSMR3PutMem(pSSM, pMI->ctxUsage, sizeof (pMI->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); } /* @todo add hashtable walker with result info and intermediate abort */ @@ -708,6 +883,9 @@ static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *d CRASSERT(pCreateInfo && pSSM); + /* Don't store default mural create info */ + if (!key) return; + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); CRASSERT(rc == VINF_SUCCESS); @@ -721,10 +899,22 @@ static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *d } } +static void crVBoxServerSaveCreateInfoFromMuralInfoCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMural = (CRMuralInfo *)data1; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pMural->CreateInfo.pszDpyName; + CreateInfo.visualBits = pMural->CreateInfo.requestedVisualBits; + CreateInfo.externalID = pMural->CreateInfo.externalID; + crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); +} + static void crVBoxServerSaveCreateInfoFromCtxInfoCB(unsigned long key, void *data1, void *data2) { CRContextInfo *pContextInfo = (CRContextInfo *)data1; - CRCreateInfo_t CreateInfo = pContextInfo->CreateInfo; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pContextInfo->CreateInfo.pszDpyName; + CreateInfo.visualBits = pContextInfo->CreateInfo.requestedVisualBits; /* saved state contains internal id */ CreateInfo.externalID = pContextInfo->pContext->id; crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); @@ -739,40 +929,672 @@ static void crVBoxServerSyncTextureCB(unsigned long key, void *data1, void *data crStateTextureObjectDiff(pContext, NULL, NULL, pTexture, GL_TRUE); } +typedef struct CRVBOX_SAVE_STATE_GLOBAL +{ + /* context id -> mural association + * on context data save, each context will be made current with the corresponding mural from this table + * thus saving the mural front & back buffer data */ + CRHashTable *contextMuralTable; + /* mural id -> context info + * for murals that do not have associated context in contextMuralTable + * we still need to save*/ + CRHashTable *additionalMuralContextTable; + + PSSMHANDLE pSSM; + + int rc; +} CRVBOX_SAVE_STATE_GLOBAL, *PCRVBOX_SAVE_STATE_GLOBAL; + + +typedef struct CRVBOX_CTXWND_CTXWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + GLuint cAdditionalMurals; +} CRVBOX_CTXWND_CTXWALKER_CB, *PCRVBOX_CTXWND_CTXWALKER_CB; + +static void crVBoxServerBuildAdditionalWindowContextMapCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRContextInfo *pContextInfo = NULL; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + { + Assert(crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + return; + } + + Assert(!crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + + if (cr_server.MainContextInfo.CreateInfo.realVisualBits == pMural->CreateInfo.realVisualBits) + { + pContextInfo = &cr_server.MainContextInfo; + } + else + { + crWarning("different visual bits not implemented!"); + pContextInfo = &cr_server.MainContextInfo; + } + + crHashtableAdd(pData->pGlobal->additionalMuralContextTable, pMural->CreateInfo.externalID, pContextInfo); +} + + +typedef struct CRVBOX_CTXWND_WNDWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + CRContextInfo *pContextInfo; + CRMuralInfo * pMural; +} CRVBOX_CTXWND_WNDWALKER_CB, *PCRVBOX_CTXWND_WNDWALKER_CB; + +static void crVBoxServerBuildContextWindowMapWindowWalkerCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_WNDWALKER_CB pData = (PCRVBOX_CTXWND_WNDWALKER_CB)data2; + + Assert(pData->pMural != pMural); + Assert(pData->pContextInfo); + + if (pData->pMural) + return; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (!CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pData->pContextInfo->pContext)) + return; + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + return; + + CRASSERT(pMural->CreateInfo.realVisualBits == pData->pContextInfo->CreateInfo.realVisualBits); + pData->pMural = pMural; +} + +static void crVBoxServerBuildContextUsedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + + if (!pContextInfo->currentMural) + return; + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pContextInfo->currentMural); + crHashtableAdd(pData->usedMuralTable, pContextInfo->currentMural->CreateInfo.externalID, pContextInfo->currentMural); +} + +CRMuralInfo * crServerGetDummyMural(GLint visualBits) +{ + CRMuralInfo * pMural = (CRMuralInfo *)crHashtableSearch(cr_server.dummyMuralTable, visualBits); + if (!pMural) + { + GLint id; + pMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + if (!pMural) + { + crWarning("crCalloc failed!"); + return NULL; + } + id = crServerMuralInit(pMural, GL_FALSE, visualBits, 0); + if (id < 0) + { + crWarning("crServerMuralInit failed!"); + crFree(pMural); + return NULL; + } + + crHashtableAdd(cr_server.dummyMuralTable, visualBits, pMural); + } + + return pMural; +} + +static void crVBoxServerBuildContextUnusedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRMuralInfo * pMural = NULL; + + if (pContextInfo->currentMural) + return; + + Assert(crHashtableNumElements(pData->pGlobal->contextMuralTable) <= crHashtableNumElements(cr_server.muralTable) - 1); + if (crHashtableNumElements(pData->pGlobal->contextMuralTable) < crHashtableNumElements(cr_server.muralTable) - 1) + { + CRVBOX_CTXWND_WNDWALKER_CB MuralData; + MuralData.pGlobal = pData->pGlobal; + MuralData.usedMuralTable = pData->usedMuralTable; + MuralData.pContextInfo = pContextInfo; + MuralData.pMural = NULL; + + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildContextWindowMapWindowWalkerCB, &MuralData); + + pMural = MuralData.pMural; + + } + + if (!pMural) + { + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + if (!pMural) + { + crWarning("crServerGetDummyMural failed"); + return; + } + } + else + { + crHashtableAdd(pData->usedMuralTable, pMural->CreateInfo.externalID, pMural); + ++pData->cAdditionalMurals; + } + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pMural); +} + +static void crVBoxServerBuildSaveStateGlobal(PCRVBOX_SAVE_STATE_GLOBAL pGlobal) +{ + CRVBOX_CTXWND_CTXWALKER_CB Data; + GLuint cMurals; + pGlobal->contextMuralTable = crAllocHashtable(); + pGlobal->additionalMuralContextTable = crAllocHashtable(); + /* 1. go through all contexts and match all having currentMural set */ + Data.pGlobal = pGlobal; + Data.usedMuralTable = crAllocHashtable(); + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUsedWindowMapCB, &Data); + + cMurals = crHashtableNumElements(pGlobal->contextMuralTable); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + CRASSERT(cMurals == crHashtableNumElements(Data.usedMuralTable)); + if (cMurals < crHashtableNumElements(cr_server.contextTable)) + { + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUnusedWindowMapCB, &Data); + } + + CRASSERT(crHashtableNumElements(pGlobal->contextMuralTable) == crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals + Data.cAdditionalMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + if (cMurals + Data.cAdditionalMurals < crHashtableNumElements(cr_server.muralTable) - 1) + { + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildAdditionalWindowContextMapCB, &Data); + CRASSERT(cMurals + Data.cAdditionalMurals + crHashtableNumElements(pGlobal->additionalMuralContextTable) == crHashtableNumElements(cr_server.muralTable) - 1); + } + + crFreeHashtable(Data.usedMuralTable, NULL); +} + +static void crVBoxServerFBImageDataTerm(CRFBData *pData) +{ + GLuint i; + for (i = 0; i < pData->cElements; ++i) + { + CRFBDataElement * pEl = &pData->aElements[i]; + if (pEl->pvData) + { + crFree(pEl->pvData); + /* sanity */ + pEl->pvData = NULL; + } + } + pData->cElements = 0; +} + +static int crVBoxServerFBImageDataInitEx(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite, uint32_t version, GLuint overrideWidth, GLuint overrideHeight) +{ + CRContext *pContext; + GLuint i; + GLfloat *pF; + CRFBDataElement *pEl; + GLuint width; + GLuint height; + + crMemset(pData, 0, sizeof (*pData)); + + pContext = pCtxInfo->pContext; + + /* the version should be always actual when we do reads, + * i.e. it could differ on writes when snapshot is getting loaded */ + CRASSERT(fWrite || version == SHCROGL_SSM_VERSION); + + width = overrideWidth ? overrideWidth : pMural->width; + height = overrideHeight ? overrideHeight : pMural->height; + + if (!width || !height) + return VINF_SUCCESS; + + if (pMural) + { + if (fWrite) + { + if (!pContext->framebufferobject.drawFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurDrawBuffer); + } + else + { + if (!pContext->framebufferobject.readFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurReadBuffer); + } + } + pData->cElements = 0; + + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_FRONT; + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_RGBA; + pEl->enmType = GL_UNSIGNED_BYTE; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + + /* there is a lot of code that assumes we have double buffering, just assert here to print a warning in the log + * so that we know that something irregular is going on */ + CRASSERT(pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT); + if ((pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT) + || version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + ) + { + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_BB_IDX(pMural)] : 0; + pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_BACK; + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_RGBA; + pEl->enmType = GL_UNSIGNED_BYTE; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + + if (version < SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + return VINF_SUCCESS; + + + if (version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL) + { +/* if (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT) */ /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + { + AssertCompile(sizeof (GLfloat) == 4); + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_DEPTH_COMPONENT; + pEl->enmType = GL_FLOAT; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + + /* init to default depth value, just in case */ + pF = (GLfloat*)pEl->pvData; + for (i = 0; i < width * height; ++i) + { + pF[i] = 1.; + } + ++pData->cElements; + } + + /* if (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) */ /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + { + AssertCompile(sizeof (GLuint) == 4); + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_STENCIL_INDEX; + pEl->enmType = GL_UNSIGNED_INT; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + return VINF_SUCCESS; + } + + if ((pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) + || (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT)) + { + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_DEPTH_STENCIL; + pEl->enmType = GL_UNSIGNED_INT_24_8; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + return VINF_SUCCESS; +} + +static int crVBoxServerFBImageDataInit(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite) +{ + return crVBoxServerFBImageDataInitEx(pData, pCtxInfo, pMural, fWrite, SHCROGL_SSM_VERSION, 0, 0); +} + +static int crVBoxServerSaveFBImage(PSSMHANDLE pSSM) +{ + CRContextInfo *pCtxInfo; + CRContext *pContext; + CRMuralInfo *pMural; + int32_t rc; + GLuint i; + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4])); + + pCtxInfo = cr_server.currentCtxInfo; + pContext = pCtxInfo->pContext; + pMural = pCtxInfo->currentMural; + + rc = crVBoxServerFBImageDataInit(&Data.data, pCtxInfo, pMural, GL_FALSE); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + + rc = crStateAcquireFBImage(pContext, &Data.data); + AssertRCReturn(rc, rc); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3PutMem(pSSM, pEl->pvData, pEl->cbData); + AssertRCReturn(rc, rc); + } + + crVBoxServerFBImageDataTerm(&Data.data); + + return VINF_SUCCESS; +} + +#define CRSERVER_ASSERTRC_RETURN_VOID(_rc) do { \ + if(!RT_SUCCESS((_rc))) { \ + AssertFailed(); \ + return; \ + } \ + } while (0) + +static void crVBoxServerSaveAdditionalMuralsCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *) data1; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + PSSMHANDLE pSSM = pData->pSSM; + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural = pContextInfo->currentMural; + + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &pContextInfo->CreateInfo.externalID, sizeof(pContextInfo->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + crServerPerformMakeCurrent(pMural, pContextInfo); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); +} + static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2) { CRContextInfo *pContextInfo = (CRContextInfo *) data1; CRContext *pContext = pContextInfo->pContext; - PSSMHANDLE pSSM = (PSSMHANDLE) data2; - int32_t rc; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + PSSMHANDLE pSSM = pData->pSSM; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(pData->contextMuralTable, key); + CRMuralInfo *pContextCurrentMural = pContextInfo->currentMural; + const int32_t i32Dummy = 0; + + AssertCompile(sizeof (i32Dummy) == sizeof (pMural->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); CRASSERT(pContext && pSSM); + CRASSERT(pMural); + CRASSERT(pMural->CreateInfo.externalID); /* We could have skipped saving the key and use similar callback to load context states back, * but there's no guarantee we'd traverse hashtable in same order after loading. */ - rc = SSMR3PutMem(pSSM, &key, sizeof(key)); - CRASSERT(rc == VINF_SUCCESS); + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + +#ifdef DEBUG_misha + { + unsigned long id; + if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id)) + crWarning("No client id for server ctx %d", pContextInfo->CreateInfo.externalID); + else + CRASSERT(id == key); + } +#endif #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - if (cr_server.curClient) + if (pContextInfo->currentMural + || crHashtableSearch(cr_server.muralTable, pMural->CreateInfo.externalID) /* <- this is not a dummy mural */ + ) + { + CRASSERT(pMural->CreateInfo.externalID); + CRASSERT(!crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &pMural->CreateInfo.externalID, sizeof(pMural->CreateInfo.externalID)); + } + else + { + /* this is a dummy mural */ + CRASSERT(!pMural->width); + CRASSERT(!pMural->height); + CRASSERT(crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &i32Dummy, sizeof(pMural->CreateInfo.externalID)); + } + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + CRASSERT(CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pContext)); + CRASSERT(pContextInfo->currentMural == pMural || !pContextInfo->currentMural); + CRASSERT(cr_server.curClient); + + crServerPerformMakeCurrent(pMural, pContextInfo); +#endif + + pData->rc = crStateSaveContext(pContext, pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + /* restore the initial current mural */ + pContextInfo->currentMural = pContextCurrentMural; +} + +#if 0 +typedef struct CR_SERVER_CHECK_BUFFERS +{ + CRBufferObject *obj; + CRContext *ctx; +}CR_SERVER_CHECK_BUFFERS, *PCR_SERVER_CHECK_BUFFERS; + +static void crVBoxServerCheckConsistencyContextBuffersCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo* pContextInfo = (CRContextInfo*)data1; + CRContext *ctx = pContextInfo->pContext; + PCR_SERVER_CHECK_BUFFERS pBuffers = (PCR_SERVER_CHECK_BUFFERS)data2; + CRBufferObject *obj = pBuffers->obj; + CRBufferObjectState *b = &(ctx->bufferobject); + int j, k; + + if (obj == b->arrayBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } + if (obj == b->elementsBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } +#ifdef CR_ARB_pixel_buffer_object + if (obj == b->packBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } + if (obj == b->unpackBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } +#endif + +#ifdef CR_ARB_vertex_buffer_object + for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j) { - unsigned long id; - if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id)) + CRClientPointer *cp = crStateGetClientPointerByIndex(j, &ctx->client.array); + if (obj == cp->buffer) { - crWarning("No client id for server ctx %d", pContext->id); + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; } - else + } + + for (k=0; k<ctx->client.vertexArrayStackDepth; ++k) + { + CRVertexArrays *pArray = &ctx->client.vertexArrayStack[k]; + for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j) { - crServerDispatchMakeCurrent(cr_server.curClient->currentWindow, 0, id); + CRClientPointer *cp = crStateGetClientPointerByIndex(j, pArray); + if (obj == cp->buffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } } } #endif +} - rc = crStateSaveContext(pContext, pSSM); - CRASSERT(rc == VINF_SUCCESS); +static void crVBoxServerCheckConsistencyBuffersCB(unsigned long key, void *data1, void *data2) +{ + CRBufferObject *obj = (CRBufferObject *)data1; + CR_SERVER_CHECK_BUFFERS Buffers = {0}; + Buffers.obj = obj; + crHashtableWalk(cr_server.contextTable, crVBoxServerCheckConsistencyContextBuffersCB, (void*)&Buffers); } +//static void crVBoxServerCheckConsistency2CB(unsigned long key, void *data1, void *data2) +//{ +// CRContextInfo* pContextInfo1 = (CRContextInfo*)data1; +// CRContextInfo* pContextInfo2 = (CRContextInfo*)data2; +// +// CRASSERT(pContextInfo1->pContext); +// CRASSERT(pContextInfo2->pContext); +// +// if (pContextInfo1 == pContextInfo2) +// { +// CRASSERT(pContextInfo1->pContext == pContextInfo2->pContext); +// return; +// } +// +// CRASSERT(pContextInfo1->pContext != pContextInfo2->pContext); +// CRASSERT(pContextInfo1->pContext->shared); +// CRASSERT(pContextInfo2->pContext->shared); +// CRASSERT(pContextInfo1->pContext->shared == pContextInfo2->pContext->shared); +// if (pContextInfo1->pContext->shared != pContextInfo2->pContext->shared) +// return; +// +// crHashtableWalk(pContextInfo1->pContext->shared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, pContextInfo2); +//} +static void crVBoxServerCheckSharedCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo* pContextInfo = (CRContextInfo*)data1; + void **ppShared = (void**)data2; + if (!*ppShared) + *ppShared = pContextInfo->pContext->shared; + else + Assert(pContextInfo->pContext->shared == *ppShared); +} + +static void crVBoxServerCheckConsistency() +{ + CRSharedState *pShared = NULL; + crHashtableWalk(cr_server.contextTable, crVBoxServerCheckSharedCB, (void*)&pShared); + Assert(pShared); + if (pShared) + { + crHashtableWalk(pShared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, NULL); + } +} +#endif + static uint32_t g_hackVBoxServerSaveLoadCallsLeft = 0; DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) @@ -781,8 +1603,18 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) uint32_t ui32; GLboolean b; unsigned long key; + GLenum err; #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - unsigned long ctxID=-1, winID=-1; + CRClient *curClient; + CRMuralInfo *curMural = NULL; + CRContextInfo *curCtxInfo = NULL; +#endif + CRVBOX_SAVE_STATE_GLOBAL Data; + + crMemset(&Data, 0, sizeof (Data)); + +#if 0 + crVBoxServerCheckConsistency(); #endif /* We shouldn't be called if there's no clients at all*/ @@ -811,6 +1643,14 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) return VINF_SUCCESS; } +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_SAVE_START "VBox.Cr.StateSaveStart" +#define CR_DBG_STR_STATE_SAVE_STOP "VBox.Cr.StateSaveStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_START), CR_DBG_STR_STATE_SAVE_START); +#endif + /* Save rendering contexts creation info */ ui32 = crHashtableNumElements(cr_server.contextTable); rc = SSMR3PutU32(pSSM, (uint32_t) ui32); @@ -818,50 +1658,71 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) crHashtableWalk(cr_server.contextTable, crVBoxServerSaveCreateInfoFromCtxInfoCB, pSSM); #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + curClient = cr_server.curClient; /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */ - if (cr_server.curClient) + if (curClient) { - ctxID = cr_server.curClient->currentContextNumber; - winID = cr_server.curClient->currentWindow; + curCtxInfo = cr_server.curClient->currentCtxInfo; + curMural = cr_server.curClient->currentMural; } -#endif - - /* Save contexts state tracker data */ - /* @todo For now just some blind data dumps, - * but I've a feeling those should be saved/restored in a very strict sequence to - * allow diff_api to work correctly. - * Should be tested more with multiply guest opengl apps working when saving VM snapshot. - */ - crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, pSSM); - -#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - /* Restore original win and ctx IDs*/ - if (cr_server.curClient) + else if (cr_server.numClients) { - crServerDispatchMakeCurrent(winID, 0, ctxID); + cr_server.curClient = cr_server.clients[0]; } #endif + /* first save windows info */ /* Save windows creation info */ - ui32 = crHashtableNumElements(cr_server.pWindowCreateInfoTable); - rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + ui32 = crHashtableNumElements(cr_server.muralTable); + /* There should be default mural always */ + CRASSERT(ui32>=1); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); AssertRCReturn(rc, rc); - crHashtableWalk(cr_server.pWindowCreateInfoTable, crVBoxServerSaveCreateInfoCB, pSSM); + crHashtableWalk(cr_server.muralTable, crVBoxServerSaveCreateInfoFromMuralInfoCB, pSSM); /* Save cr_server.muralTable * @todo we don't need it all, just geometry info actually */ - ui32 = crHashtableNumElements(cr_server.muralTable); - /* There should be default mural always */ - CRASSERT(ui32>=1); rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); AssertRCReturn(rc, rc); crHashtableWalk(cr_server.muralTable, crVBoxServerSaveMuralCB, pSSM); - /* Save starting free context and window IDs */ - rc = SSMR3PutMem(pSSM, &cr_server.idsPool, sizeof(cr_server.idsPool)); + /* we need to save front & backbuffer data for each mural first create a context -> mural association */ + crVBoxServerBuildSaveStateGlobal(&Data); + + rc = crStateSaveGlobals(pSSM); AssertRCReturn(rc, rc); + Data.pSSM = pSSM; + /* Save contexts state tracker data */ + /* @todo For now just some blind data dumps, + * but I've a feeling those should be saved/restored in a very strict sequence to + * allow diff_api to work correctly. + * Should be tested more with multiply guest opengl apps working when saving VM snapshot. + */ + crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + + ui32 = crHashtableNumElements(Data.additionalMuralContextTable); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + AssertRCReturn(rc, rc); + + crHashtableWalk(Data.additionalMuralContextTable, crVBoxServerSaveAdditionalMuralsCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + cr_server.curClient = curClient; + /* Restore original win and ctx IDs*/ + if (curClient && curMural && curCtxInfo) + { + crServerPerformMakeCurrent(curMural, curCtxInfo); + } + else + { + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + } +#endif + /* Save clients info */ for (i = 0; i < cr_server.numClients; i++) { @@ -881,7 +1742,7 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient)); AssertRCReturn(rc, rc); - if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber>=0) + if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber > 0) { b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtxInfo, &key); CRASSERT(b); @@ -889,7 +1750,7 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) AssertRCReturn(rc, rc); } - if (pClient->currentMural && pClient->currentWindow>=0) + if (pClient->currentMural && pClient->currentWindow > 0) { b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key); CRASSERT(b); @@ -899,8 +1760,21 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) } } + rc = CrPMgrSaveState(pSSM); + AssertRCReturn(rc, rc); + + /* all context gl error states should have now be synced with chromium erro states, + * reset the error if any */ + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after saving snapshot", err); + cr_server.bIsInSavingState = GL_FALSE; +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_STOP), CR_DBG_STR_STATE_SAVE_STOP); +#endif + return VINF_SUCCESS; } @@ -912,11 +1786,527 @@ static DECLCALLBACK(CRContext*) crVBoxServerGetContextCB(void* pvData) return pContextInfo->pContext; } +typedef struct CR_SERVER_LOADSTATE_READER +{ + PSSMHANDLE pSSM; + uint32_t cbBuffer; + uint32_t cbData; + uint32_t offData; + uint8_t *pu8Buffer; +} CR_SERVER_LOADSTATE_READER; + +static void crServerLsrInit(CR_SERVER_LOADSTATE_READER *pReader, PSSMHANDLE pSSM) +{ + memset(pReader, 0, sizeof (*pReader)); + pReader->pSSM = pSSM; +} + +static void crServerLsrTerm(CR_SERVER_LOADSTATE_READER *pReader) +{ + if (pReader->pu8Buffer) + RTMemFree(pReader->pu8Buffer); + + /* sanity */ + memset(pReader, 0, sizeof (*pReader)); +} + +static int crServerLsrDataGetMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + int rc = VINF_SUCCESS; + uint32_t cbRemaining = cbBuffer; + if (pReader->cbData) + { + uint8_t cbData = RT_MIN(pReader->cbData, cbBuffer); + memcpy(pvBuffer, pReader->pu8Buffer + pReader->offData, cbData); + pReader->cbData -= cbData; + pReader->offData += cbData; + + cbRemaining -= cbData; + pvBuffer = ((uint8_t*)pvBuffer) + cbData; + } + + if (cbRemaining) + { + rc = SSMR3GetMem(pReader->pSSM, pvBuffer, cbRemaining); + AssertRC(rc); + } + + return rc; +} + +static int crServerLsrDataGetU32(CR_SERVER_LOADSTATE_READER *pReader, uint32_t *pu32) +{ + return crServerLsrDataGetMem(pReader, pu32, sizeof (*pu32)); +} + +static int crServerLsrDataPutMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + if (!pReader->cbData && pReader->cbBuffer >= cbBuffer) + { + pReader->offData = 0; + pReader->cbData = cbBuffer; + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + } + else if (pReader->offData >= cbBuffer) + { + pReader->offData -= cbBuffer; + pReader->cbData += cbBuffer; + memcpy(pReader->pu8Buffer + pReader->offData, pvBuffer, cbBuffer); + } + else + { + uint8_t *pu8Buffer = pReader->pu8Buffer; + + pReader->pu8Buffer = (uint8_t*)RTMemAlloc(cbBuffer + pReader->cbData); + if (!pReader->pu8Buffer) + { + crWarning("failed to allocate mem %d", cbBuffer + pReader->cbData); + return VERR_NO_MEMORY; + } + + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + if (pu8Buffer) + { + memcpy(pReader->pu8Buffer + cbBuffer, pu8Buffer + pReader->offData, pReader->cbData); + RTMemFree(pu8Buffer); + } + else + { + Assert(!pReader->cbData); + } + pReader->offData = 0; + pReader->cbData += cbBuffer; + } + + return VINF_SUCCESS; +} + +/* data to be skipped */ + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_2 +{ + void*ListHead_pNext; + void*ListHead_pPrev; + uint32_t cEntries; +} CR_SERVER_BUGGY_MURAL_DATA_2; +typedef struct CR_SERVER_BUGGY_MURAL_DATA_1 +{ + /* VBOXVR_COMPOSITOR_ENTRY Ce; */ + void*Ce_Node_pNext; + void*Ce_Node_pPrev; + CR_SERVER_BUGGY_MURAL_DATA_2 Vr; + /* VBOXVR_TEXTURE Tex; */ + uint32_t Tex_width; + uint32_t Tex_height; + uint32_t Tex_target; + uint32_t Tex_hwid; + /* RTPOINT Pos; */ + uint32_t Pos_x; + uint32_t Pos_y; + uint32_t fChanged; + uint32_t cRects; + void* paSrcRects; + void* paDstRects; +} CR_SERVER_BUGGY_MURAL_DATA_1; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_4 +{ + uint32_t u32Magic; + int32_t cLockers; + RTNATIVETHREAD NativeThreadOwner; + int32_t cNestings; + uint32_t fFlags; + void* EventSem; + R3R0PTRTYPE(PRTLOCKVALRECEXCL) pValidatorRec; + RTHCPTR Alignment; +} CR_SERVER_BUGGY_MURAL_DATA_4; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_3 +{ + void*Compositor_List_pNext; + void*Compositor_List_pPrev; + void*Compositor_pfnEntryRemoved; + float StretchX; + float StretchY; + uint32_t cRects; + uint32_t cRectsBuffer; + void*paSrcRects; + void*paDstRects; + CR_SERVER_BUGGY_MURAL_DATA_4 CritSect; +} CR_SERVER_BUGGY_MURAL_DATA_3; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA +{ + uint8_t fRootVrOn; + CR_SERVER_BUGGY_MURAL_DATA_1 RootVrCEntry; + CR_SERVER_BUGGY_MURAL_DATA_3 RootVrCompositor; +} CR_SERVER_BUGGY_MURAL_DATA; + +AssertCompile(sizeof (CR_SERVER_BUGGY_MURAL_DATA) < sizeof (CRClient)); + +static int32_t crVBoxServerLoadMurals(CR_SERVER_LOADSTATE_READER *pReader, uint32_t version) +{ + unsigned long key; + uint32_t ui, uiNumElems; + bool fBuggyMuralData = false; + /* Load windows */ + int32_t rc = crServerLsrDataGetU32(pReader, &uiNumElems); + AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRCreateInfo_t createInfo; + char psz[200]; + GLint winID; + unsigned long key; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &createInfo, sizeof(createInfo)); + AssertRCReturn(rc, rc); + + CRASSERT(!pReader->cbData); + + if (createInfo.pszDpyName) + { + rc = SSMR3GetStrZEx(pReader->pSSM, psz, 200, NULL); + AssertRCReturn(rc, rc); + createInfo.pszDpyName = psz; + } + + winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key); + CRASSERT((int64_t)winID == (int64_t)key); + } + + /* Load cr_server.muralTable */ + rc = SSMR3GetU32(pReader->pSSM, &uiNumElems); + AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRMuralInfo muralInfo; + CRMuralInfo *pActualMural = NULL; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &muralInfo, RT_OFFSETOF(CRMuralInfo, CreateInfo)); + AssertRCReturn(rc, rc); + + if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING) + muralInfo.bFbDraw = GL_TRUE; + + if (!ui && version == SHCROGL_SSM_VERSION_WITH_BUGGY_MURAL_INFO) + { + /* Lookahead buffer used to determine whether the data erroneously stored root visible regions data */ + union + { + void * apv[1]; + CR_SERVER_BUGGY_MURAL_DATA Data; + /* need to chak spuWindow, so taking the offset of filed following it*/ + uint8_t au8[RT_OFFSETOF(CRMuralInfo, screenId)]; + RTRECT aVisRects[sizeof (CR_SERVER_BUGGY_MURAL_DATA) / sizeof (RTRECT)]; + } LaBuf; + + do { + /* first value is bool (uint8_t) value followed by pointer-size-based alignment. + * the mural memory is zero-initialized initially, so we can be sure the padding is zeroed */ + rc = crServerLsrDataGetMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertRCReturn(rc, rc); + if (LaBuf.apv[0] != NULL && LaBuf.apv[0] != ((void*)1)) + break; + + /* check that the pointers are either valid or NULL */ + if(LaBuf.Data.RootVrCEntry.Ce_Node_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev)) + break; + + /* the entry can can be the only one within the (mural) compositor, + * so its compositor entry node can either contain NULL pNext and pPrev, + * or both of them pointing to compositor's list head */ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext != LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* can either both or none be NULL */ + if (!LaBuf.Data.RootVrCEntry.Ce_Node_pNext != !LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + if (!LaBuf.Data.fRootVrOn) + { + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext || LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* either non-initialized (zeroed) or empty list */ + if (LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext != LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + break; + } + else + { + /* the entry should be initialized */ + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext) + break; + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + { + /* entry should be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev == NULL) + break; + CRASSERT(LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + else + { + /* entry should NOT be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev != NULL) + break; + CRASSERT(!LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + } + +#if 0 + if (muralInfo.pVisibleRects) + { + int j; + int cRects = RT_MIN(muralInfo.cVisibleRects, RT_ELEMENTS(LaBuf.aVisRects)); + CRASSERT(cRects); + for (j = 0; j < cRects; ++j) + { + PRTRECT pRect = &LaBuf.aVisRects[j]; + if (pRect->xLeft >= pRect->xRight) + break; + if (pRect->yTop >= pRect->yBottom) + break; + if (pRect->xLeft < 0 || pRect->xRight < 0 + || pRect->yTop < 0 || pRect->yBottom < 0) + break; + if (pRect->xLeft > (GLint)muralInfo.width + || pRect->xRight > (GLint)muralInfo.width) + break; + if (pRect->yTop > (GLint)muralInfo.height + || pRect->yBottom > (GLint)muralInfo.height) + break; + } + + if (j < cRects) + { + fBuggyMuralData = true; + break; + } + } + + if (muralInfo.pVisibleRects) + { + /* @todo: do we actually need any further checks here? */ + fBuggyMuralData = true; + break; + } + + /* no visible regions*/ + + if (ui == uiNumElems - 1) + { + /* this is the last mural, next it goes idsPool, whose content can not match the above template again */ + fBuggyMuralData = true; + break; + } + + /* next it goes a next mural info */ +// if (!fExpectPtr) +// { +// CRMuralInfo *pNextSpuWindowInfoMural = (CRMuralInfo*)((void*)&LaBuf); +// if (!pNextSpuWindowInfoMural->spuWindow) +// fBuggyMuralData = true; +// +// break; +// } +#endif + /* fExpectPtr == true, the valid pointer values should not match possible mural width/height/position */ + fBuggyMuralData = true; + break; + + } while (0); + + rc = crServerLsrDataPutMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertRCReturn(rc, rc); + } + + if (fBuggyMuralData) + { + CR_SERVER_BUGGY_MURAL_DATA Tmp; + rc = crServerLsrDataGetMem(pReader, &Tmp, sizeof (Tmp)); + AssertRCReturn(rc, rc); + } + + if (muralInfo.pVisibleRects) + { + muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); + if (!muralInfo.pVisibleRects) + { + return VERR_NO_MEMORY; + } + + rc = crServerLsrDataGetMem(pReader, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); + AssertRCReturn(rc, rc); + } + + pActualMural = (CRMuralInfo *)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pActualMural); + + if (version >= SHCROGL_SSM_VERSION_WITH_WINDOW_CTX_USAGE) + { + rc = crServerLsrDataGetMem(pReader, pActualMural->ctxUsage, sizeof (pActualMural->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); + } + + /* Restore windows geometry info */ + crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height); + crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY); + /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/ + if (muralInfo.bReceivedRects) + { + crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects); + } + crServerDispatchWindowShow(key, muralInfo.bVisible); + + if (muralInfo.pVisibleRects) + { + crFree(muralInfo.pVisibleRects); + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + +static int crVBoxServerLoadFBImage(PSSMHANDLE pSSM, uint32_t version, + CRContextInfo* pContextInfo, CRMuralInfo *pMural) +{ + CRContext *pContext = pContextInfo->pContext; + int32_t rc = VINF_SUCCESS; + GLuint i; + /* can apply the data right away */ + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4])); + + if (version >= SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + { + if (!pMural->width || !pMural->height) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, 0, 0); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + else + { + GLint storedWidth, storedHeight; + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(cr_server.currentCtxInfo == pContextInfo); + CRASSERT(cr_server.currentMural == pMural); + storedWidth = pMural->width; + storedHeight = pMural->height; + } + else + { + storedWidth = pContext->buffer.storedWidth; + storedHeight = pContext->buffer.storedHeight; + } + + if (!storedWidth || !storedHeight) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, storedWidth, storedHeight); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + + CRASSERT(Data.data.cElements); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3GetMem(pSSM, pEl->pvData, pEl->cbData); + AssertRCReturn(rc, rc); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRBufferState *pBuf = &pContext->buffer; + /* can apply the data right away */ + CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo); + CRASSERT(cr_server.currentMural); + + cr_server.head_spu->dispatch_table.MakeCurrent( pMural->spuWindow, + 0, + pContextInfo->SpuContext >= 0 + ? pContextInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + crStateApplyFBImage(pContext, &Data.data); + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + crVBoxServerFBImageDataTerm(&Data.data); + + crServerPresentFBO(pMural); + + CRASSERT(cr_server.currentMural); + cr_server.head_spu->dispatch_table.MakeCurrent( cr_server.currentMural->spuWindow, + 0, + cr_server.currentCtxInfo->SpuContext >= 0 + ? cr_server.currentCtxInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + } + else + { + CRBufferState *pBuf = &pContext->buffer; + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + CRASSERT(Data.data.cElements); /* <- older versions always saved front and back, and we filtered out the null-sized buffers above */ + + if (Data.data.cElements) + { + CRFBData *pLazyData = crAlloc(RT_OFFSETOF(CRFBData, aElements[Data.data.cElements])); + if (!RT_SUCCESS(rc)) + { + crVBoxServerFBImageDataTerm(&Data.data); + crWarning("crAlloc failed"); + return VERR_NO_MEMORY; + } + + crMemcpy(pLazyData, &Data.data, RT_OFFSETOF(CRFBData, aElements[Data.data.cElements])); + pBuf->pFrontImg = pLazyData; + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) { int32_t rc, i; uint32_t ui, uiNumElems; unsigned long key; + GLenum err; + CR_SERVER_LOADSTATE_READER Reader; if (!cr_server.bIsInLoadingState) { @@ -941,6 +2331,16 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; } + crServerLsrInit(&Reader, pSSM); + +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_LOAD_START "VBox.Cr.StateLoadStart" +#define CR_DBG_STR_STATE_LOAD_STOP "VBox.Cr.StateLoadStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_START), CR_DBG_STR_STATE_LOAD_START); +#endif + /* Load and recreate rendering contexts */ rc = SSMR3GetU32(pSSM, &uiNumElems); AssertRCReturn(rc, rc); @@ -974,11 +2374,44 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) pContext->shared->id=-1; } + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(!Reader.pu8Buffer); + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertRCReturn(rc, rc); + CRASSERT(!Reader.pu8Buffer); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA && uiNumElems) + { + /* set the current client to allow doing crServerPerformMakeCurrent later */ + CRASSERT(cr_server.numClients); + cr_server.curClient = cr_server.clients[0]; + } + + rc = crStateLoadGlobals(pSSM, version); + AssertRCReturn(rc, rc); + + if (uiNumElems) + { + /* ensure we have main context set up as current */ + CRMuralInfo *pMural; + CRASSERT(cr_server.MainContextInfo.SpuContext > 0); + CRASSERT(!cr_server.currentCtxInfo); + CRASSERT(!cr_server.currentMural); + pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CRASSERT(pMural); + crServerPerformMakeCurrent(pMural, &cr_server.MainContextInfo); + } + /* Restore context state data */ for (ui=0; ui<uiNumElems; ++ui) { CRContextInfo* pContextInfo; CRContext *pContext; + CRMuralInfo *pMural = NULL; + int32_t winId = 0; rc = SSMR3GetMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); @@ -988,82 +2421,91 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) CRASSERT(pContextInfo->pContext); pContext = pContextInfo->pContext; - rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version); - AssertRCReturn(rc, rc); - } - - /* Load windows */ - rc = SSMR3GetU32(pSSM, &uiNumElems); - AssertRCReturn(rc, rc); - for (ui=0; ui<uiNumElems; ++ui) - { - CRCreateInfo_t createInfo; - char psz[200]; - GLint winID; - unsigned long key; - - rc = SSMR3GetMem(pSSM, &key, sizeof(key)); - AssertRCReturn(rc, rc); - rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo)); - AssertRCReturn(rc, rc); - - if (createInfo.pszDpyName) + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) { - rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL); + rc = SSMR3GetMem(pSSM, &winId, sizeof(winId)); AssertRCReturn(rc, rc); - createInfo.pszDpyName = psz; + + if (winId) + { + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, winId); + CRASSERT(pMural); + } + else + { + /* null winId means a dummy mural, get it */ + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + CRASSERT(pMural); + } } - winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key); - CRASSERT((int64_t)winID == (int64_t)key); + rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version); + AssertRCReturn(rc, rc); + + /*Restore front/back buffer images*/ + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); + AssertRCReturn(rc, rc); } - /* Load cr_server.muralTable */ - rc = SSMR3GetU32(pSSM, &uiNumElems); - AssertRCReturn(rc, rc); - for (ui=0; ui<uiNumElems; ++ui) + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) { - CRMuralInfo muralInfo; + CRContextInfo *pContextInfo; + CRMuralInfo *pMural; + GLint ctxId; - rc = SSMR3GetMem(pSSM, &key, sizeof(key)); - AssertRCReturn(rc, rc); - rc = SSMR3GetMem(pSSM, &muralInfo, sizeof(muralInfo)); + rc = SSMR3GetU32(pSSM, &uiNumElems); AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural; - if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING) - muralInfo.bFbDraw = GL_TRUE; + rc = SSMR3GetMem(pSSM, &key, sizeof(key)); + AssertRCReturn(rc, rc); - if (muralInfo.pVisibleRects) - { - muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); - if (!muralInfo.pVisibleRects) + rc = SSMR3GetMem(pSSM, &ctxId, sizeof(ctxId)); + AssertRCReturn(rc, rc); + + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pMural); + if (ctxId) { - return VERR_NO_MEMORY; + pContextInfo = (CRContextInfo *)crHashtableSearch(cr_server.contextTable, ctxId); + CRASSERT(pContextInfo); } + else + pContextInfo = &cr_server.MainContextInfo; - rc = SSMR3GetMem(pSSM, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + pInitialCurMural = pContextInfo->currentMural; + + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); AssertRCReturn(rc, rc); - } - /* Restore windows geometry info */ - crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height); - crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY); - /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/ - if (muralInfo.bReceivedRects) - { - crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects); + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; } - crServerDispatchWindowShow(key, muralInfo.bVisible); - if (muralInfo.pVisibleRects) - { - crFree(muralInfo.pVisibleRects); - } + CRASSERT(!uiNumElems || cr_server.currentCtxInfo == &cr_server.MainContextInfo); + + cr_server.curClient = NULL; + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; } + else + { + CRServerFreeIDsPool_t dummyIdsPool; - /* Load starting free context and window IDs */ - rc = SSMR3GetMem(pSSM, &cr_server.idsPool, sizeof(cr_server.idsPool)); - CRASSERT(rc == VINF_SUCCESS); + CRASSERT(!Reader.pu8Buffer); + + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertRCReturn(rc, rc); + + /* not used any more, just read it out and ignore */ + rc = crServerLsrDataGetMem(&Reader, &dummyIdsPool, sizeof(dummyIdsPool)); + CRASSERT(rc == VINF_SUCCESS); + } /* Load clients info */ for (i = 0; i < cr_server.numClients; i++) @@ -1074,21 +2516,21 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) CRClient client; unsigned long ctxID=-1, winID=-1; - rc = SSMR3GetU32(pSSM, &ui); + rc = crServerLsrDataGetU32(&Reader, &ui); AssertRCReturn(rc, rc); /* If this assert fires, then we should search correct client in the list first*/ CRASSERT(ui == pClient->conn->u32ClientID); if (version>=4) { - rc = SSMR3GetU32(pSSM, &pClient->conn->vMajor); + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMajor); AssertRCReturn(rc, rc); - rc = SSMR3GetU32(pSSM, &pClient->conn->vMinor); + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMinor); AssertRCReturn(rc, rc); } - rc = SSMR3GetMem(pSSM, &client, sizeof(client)); + rc = crServerLsrDataGetMem(&Reader, &client, sizeof(client)); CRASSERT(rc == VINF_SUCCESS); client.conn = pClient->conn; @@ -1105,9 +2547,9 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) cr_server.curClient = pClient; - if (client.currentCtxInfo && client.currentContextNumber>=0) + if (client.currentCtxInfo && client.currentContextNumber > 0) { - rc = SSMR3GetMem(pSSM, &ctxID, sizeof(ctxID)); + rc = crServerLsrDataGetMem(&Reader, &ctxID, sizeof(ctxID)); AssertRCReturn(rc, rc); client.currentCtxInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, ctxID); CRASSERT(client.currentCtxInfo); @@ -1116,9 +2558,9 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) //pClient->currentContextNumber = ctxID; } - if (client.currentMural && client.currentWindow>=0) + if (client.currentMural && client.currentWindow > 0) { - rc = SSMR3GetMem(pSSM, &winID, sizeof(winID)); + rc = crServerLsrDataGetMem(&Reader, &winID, sizeof(winID)); AssertRCReturn(rc, rc); client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID); CRASSERT(client.currentMural); @@ -1126,6 +2568,8 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) //pClient->currentWindow = winID; } + CRASSERT(!Reader.cbData); + /* Restore client active context and window */ crServerDispatchMakeCurrent(winID, 0, ctxID); @@ -1195,40 +2639,68 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) cr_server.curClient = NULL; + if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_INFO) { - GLenum err = crServerDispatchGetError(); - - if (err != GL_NO_ERROR) - { - crWarning("crServer: glGetError %d after loading snapshot", err); - } + rc = CrPMgrLoadState(pSSM, version); + AssertRCReturn(rc, rc); } + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after loading snapshot", err); + cr_server.bIsInLoadingState = GL_FALSE; +#if 0 + crVBoxServerCheckConsistency(); +#endif + +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_STOP), CR_DBG_STR_STATE_LOAD_STOP); +#endif + + CRASSERT(!Reader.cbData); + crServerLsrTerm(&Reader); + return VINF_SUCCESS; } #define SCREEN(i) (cr_server.screen[i]) #define MAPPED(screen) ((screen).winID != 0) -static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2) +extern DECLEXPORT(void) crServerVBoxSetNotifyEventCB(PFNCRSERVERNOTIFYEVENT pfnCb) { - CRMuralInfo *pMI = (CRMuralInfo*) data1; - int *sIndex = (int*) data2; + cr_server.pfnNotifyEventCB = pfnCb; +} - if (pMI->screenId == *sIndex) +void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void*pvData) +{ + /* this is something unexpected, but just in case */ + if (idScreen >= cr_server.screenCount) { - renderspuReparentWindow(pMI->spuWindow); + crWarning("invalid screen id %d", idScreen); + return; } + + cr_server.pfnNotifyEventCB(idScreen, uEvent, pvData); } -static void crVBoxServerCheckMuralCB(unsigned long key, void *data1, void *data2) +void crServerWindowReparent(CRMuralInfo *pMural) +{ + pMural->fHasParentWindow = !!cr_server.screen[pMural->screenId].winID; + + renderspuReparentWindow(pMural->spuWindow); +} + +static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2) { CRMuralInfo *pMI = (CRMuralInfo*) data1; - (void) data2; + int *sIndex = (int*) data2; - crServerCheckMuralGeometry(pMI); + if (pMI->screenId == *sIndex) + { + crServerWindowReparent(pMI); + } } DECLEXPORT(int32_t) crVBoxServerSetScreenCount(int sCount) @@ -1269,9 +2741,14 @@ DECLEXPORT(int32_t) crVBoxServerUnmapScreen(int sIndex) renderspuSetWindowId(0); crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); + + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + + CrPMgrScreenChanged((uint32_t)sIndex); } renderspuSetWindowId(SCREEN(0).winID); + return VINF_SUCCESS; } @@ -1296,9 +2773,9 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint renderspuSetWindowId(SCREEN(sIndex).winID); crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); - renderspuSetWindowId(SCREEN(0).winID); - crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL); + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + renderspuSetWindowId(SCREEN(0).winID); #ifndef WINDOWS /*Restore FB content for clients, which have current window on a screen being remapped*/ @@ -1310,7 +2787,7 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint cr_server.curClient = cr_server.clients[i]; if (cr_server.curClient->currentCtxInfo && cr_server.curClient->currentCtxInfo->pContext - && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg || cr_server.curClient->currentCtxInfo->pContext->buffer.pBackImg) + && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg) && cr_server.curClient->currentMural && cr_server.curClient->currentMural->screenId == sIndex && cr_server.curClient->currentCtxInfo->pContext->buffer.storedHeight == h @@ -1318,25 +2795,74 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint { int clientWindow = cr_server.curClient->currentWindow; int clientContext = cr_server.curClient->currentContextNumber; + CRFBData *pLazyData = (CRFBData *)cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg; if (clientWindow && clientWindow != cr_server.currentWindow) { crServerDispatchMakeCurrent(clientWindow, 0, clientContext); } - crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext); + crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext, pLazyData); + crStateFreeFBImageLegacy(cr_server.curClient->currentCtxInfo->pContext); } } cr_server.curClient = NULL; } #endif + CrPMgrScreenChanged((uint32_t)sIndex); + return VINF_SUCCESS; } -DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, GLint *pRects) +DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, const RTRECT *pRects) { - renderspuSetRootVisibleRegion(cRects, pRects); + int32_t rc = VINF_SUCCESS; + GLboolean fOldRootVrOn = cr_server.fRootVrOn; + + /* non-zero rects pointer indicate rects are present and switched on + * i.e. cRects==0 and pRects!=NULL means root visible regioning is ON and there are no visible regions, + * while pRects==NULL means root visible regioning is OFF, i.e. everything is visible */ + if (pRects) + { + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + rc = VBoxVrListRectsSet(&cr_server.RootVr, cRects, pRects, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrListRectsSet failed! rc %d", rc); + return rc; + } + + cr_server.fRootVrOn = GL_TRUE; + } + else + { + if (!cr_server.fRootVrOn) + return VINF_SUCCESS; + + VBoxVrListClear(&cr_server.RootVr); + + cr_server.fRootVrOn = GL_FALSE; + } + + if (!fOldRootVrOn != !cr_server.fRootVrOn) + { + rc = CrPMgrModeRootVr(cr_server.fRootVrOn); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrModeRootVr failed rc %d", rc); + return rc; + } + } + else if (cr_server.fRootVrOn) + { + rc = CrPMgrRootVrUpdate(); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrRootVrUpdate failed rc %d", rc); + return rc; + } + } return VINF_SUCCESS; } @@ -1348,21 +2874,7 @@ DECLEXPORT(void) crVBoxServerSetPresentFBOCB(PFNCRSERVERPRESENTFBO pfnPresentFBO DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value) { - if (cr_server.bForceOffscreenRendering==value) - { - return VINF_SUCCESS; - } - - if (value && !crServerSupportRedirMuralFBO()) - { - return VERR_NOT_SUPPORTED; - } - - cr_server.bForceOffscreenRendering=value; - - crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL); - - return VINF_SUCCESS; + return CrPMgrModeVrdp(value); } DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks) @@ -1371,74 +2883,502 @@ DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallb if (pCallbacks) { cr_server.outputRedirect = *pCallbacks; - cr_server.bUseOutputRedirect = true; } else { - cr_server.bUseOutputRedirect = false; + memset (&cr_server.outputRedirect, 0, sizeof (cr_server.outputRedirect)); } - // @todo dynamically intercept already existing output: - // crHashtableWalk(cr_server.muralTable, crVBoxServerOutputRedirectCB, NULL); + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h) +{ + CRScreenViewportInfo *pViewport; + RTRECT NewRect; + int rc; + + crDebug("crVBoxServerSetScreenViewport(%i)", sIndex); + + if (sIndex<0 || sIndex>=cr_server.screenCount) + { + crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex); + return VERR_INVALID_PARAMETER; + } + + NewRect.xLeft = x; + NewRect.yTop = y; + NewRect.xRight = x + w; + NewRect.yBottom = y + h; + + pViewport = &cr_server.screenVieport[sIndex]; + /*always do viewport updates no matter whether the rectangle actually changes, + * this is needed to ensure window is adjusted properly on OSX */ + pViewport->Rect = NewRect; + rc = CrPMgrViewportUpdate((uint32_t)sIndex); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrViewportUpdate failed %d", rc); + return rc; + } return VINF_SUCCESS; } -static void crVBoxServerUpdateScreenViewportCB(unsigned long key, void *data1, void *data2) +static void crVBoxServerDefaultContextSet() { - CRMuralInfo *mural = (CRMuralInfo*) data1; - int *sIndex = (int*) data2; + GLint spuWindow, spuCtx; - if (mural->screenId != *sIndex) - return; + if (cr_server.MainContextInfo.SpuContext) + { + CRMuralInfo *pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pMural) + { + WARN(("dummy mural is NULL")); + spuCtx = CR_RENDER_DEFAULT_CONTEXT_ID; + spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + } + else + { + spuCtx = cr_server.MainContextInfo.SpuContext; + spuWindow = pMural->CreateInfo.realVisualBits; + } + } + else + { + spuCtx = CR_RENDER_DEFAULT_CONTEXT_ID; + spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + } - crServerCheckMuralGeometry(mural); + cr_server.head_spu->dispatch_table.MakeCurrent(spuWindow, 0, spuCtx); } +#ifdef VBOX_WITH_CRHGSMI -DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h) +static int32_t crVBoxServerCmdVbvaCrCmdProcess(struct VBOXCMDVBVA_CRCMD_CMD *pCmd, uint32_t cbCmd) { - CRScreenViewportInfo *pVieport; - GLboolean fPosChanged, fSizeChanged; + int32_t rc; + uint32_t cBuffers = pCmd->cBuffers; + uint32_t cParams; + uint32_t cbHdr; + CRVBOXHGSMIHDR *pHdr; + uint32_t u32Function; + uint32_t u32ClientID; + CRClient *pClient; - crDebug("crVBoxServerSetScreenViewport(%i)", sIndex); + if (!g_pvVRamBase) + { + WARN(("g_pvVRamBase is not initialized")); + return VERR_INVALID_STATE; + } - if (sIndex<0 || sIndex>=cr_server.screenCount) + if (!cBuffers) { - crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex); + WARN(("zero buffers passed in!")); return VERR_INVALID_PARAMETER; } - pVieport = &cr_server.screenVieport[sIndex]; - fPosChanged = (pVieport->x != x || pVieport->y != y); - fSizeChanged = (pVieport->w != w || pVieport->h != h); + cParams = cBuffers-1; - if (!fPosChanged && !fSizeChanged) + if (cbCmd != RT_OFFSETOF(VBOXCMDVBVA_CRCMD_CMD, aBuffers[cBuffers])) { - crDebug("crVBoxServerSetScreenViewport: no changes"); - return VINF_SUCCESS; + WARN(("invalid buffer size")); + return VERR_INVALID_PARAMETER; } - if (fPosChanged) + cbHdr = pCmd->aBuffers[0].cbBuffer; + pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); + if (!pHdr) { - pVieport->x = x; - pVieport->y = y; + WARN(("invalid header buffer!")); + return VERR_INVALID_PARAMETER; + } - crHashtableWalk(cr_server.muralTable, crVBoxServerUpdateScreenViewportCB, &sIndex); + if (cbHdr < sizeof (*pHdr)) + { + WARN(("invalid header buffer size!")); + return VERR_INVALID_PARAMETER; } - if (fSizeChanged) + u32Function = pHdr->u32Function; + u32ClientID = pHdr->u32ClientID; + + switch (u32Function) { - pVieport->w = w; - pVieport->h = h; + case SHCRGL_GUEST_FN_WRITE: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + else + { + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + break; + } + + case SHCRGL_GUEST_FN_INJECT: + { + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr; + /* Fetch parameters. */ + uint32_t u32InjectClientID = pFnCmd->u32ClientID; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid inject cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32InjectClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer); + + /* Return the required buffer size always */ + pFnCmd->cbBuffer = cbBuffer; + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + /* the read command is never pended, complete it right away */ + pHdr->result = rc; + + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + + /* @todo: Verify */ + if (cParams == 2) + { + CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + VBOXCMDVBVA_CRCMD_BUFFER *pWbBuf = &pCmd->aBuffers[2]; + + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + uint32_t cbWriteback = pWbBuf->cbBuffer; + char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write_read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid write buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbWriteback); + if (!pWriteback) + { + crWarning("invalid writeback buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + pHdr->result = rc; + return VINF_SUCCESS; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_SET_VERSION: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + case SHCRGL_GUEST_FN_SET_PID: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + default: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } - /* no need to do anything here actually */ } + + /* we can be on fail only here */ + CRASSERT(RT_FAILURE(rc)); + pHdr->result = rc; + + return rc; +} + +static DECLCALLBACK(int) crVBoxCrCmdEnable(HVBOXCRCMDSVR hSvr, VBOXCRCMD_SVRENABLE_INFO *pInfo) +{ + cr_server.CrCmdClientInfo = *pInfo; + + crVBoxServerDefaultContextSet(); + return VINF_SUCCESS; } +static DECLCALLBACK(int) crVBoxCrCmdDisable(HVBOXCRCMDSVR hSvr) +{ + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + memset(&cr_server.CrCmdClientInfo, 0, sizeof (cr_server.CrCmdClientInfo)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdHostCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + return crVBoxServerHostCtl((VBOXCRCMDCTL*)pCmd, cbCmd); +} + +static DECLCALLBACK(int) crVBoxCrCmdGuestCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + VBOXCMDVBVA_3DCTL *pCtl = (VBOXCMDVBVA_3DCTL*)pCmd; + if (cbCmd < sizeof (VBOXCMDVBVA_3DCTL)) + { + WARN(("invalid buffer size")); + return VERR_INVALID_PARAMETER; + } + + switch (pCtl->u32Type) + { + case VBOXCMDVBVA3DCTL_TYPE_CONNECT: + { + VBOXCMDVBVA_3DCTL_CONNECT *pConnect = (VBOXCMDVBVA_3DCTL_CONNECT*)pCtl; + + return VERR_NOT_SUPPORTED; + } + case VBOXCMDVBVA3DCTL_TYPE_DISCONNECT: + { + return VERR_NOT_SUPPORTED; + } + case VBOXCMDVBVA3DCTL_TYPE_CMD: + { + VBOXCMDVBVA_3DCTL_CMD *p3DCmd; + if (cbCmd < sizeof (VBOXCMDVBVA_3DCTL_CMD)) + { + WARN(("invalid size")); + return VERR_INVALID_PARAMETER; + } + + p3DCmd = (VBOXCMDVBVA_3DCTL_CMD*)pCmd; + + return crVBoxCrCmdCmd(NULL, &p3DCmd->Cmd, cbCmd - RT_OFFSETOF(VBOXCMDVBVA_3DCTL_CMD, Cmd)); + } + default: + WARN(("invalid function")); + return VERR_INVALID_PARAMETER; + } +} + +static DECLCALLBACK(int) crVBoxCrCmdSaveState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM) +{ + AssertFailed(); + return VERR_NOT_IMPLEMENTED; +} + +static DECLCALLBACK(int) crVBoxCrCmdLoadState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM, uint32_t u32Version) +{ + AssertFailed(); + return VERR_NOT_IMPLEMENTED; +} + + +static DECLCALLBACK(int) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd) +{ + AssertFailed(); + switch (pCmd->u8OpCode) + { + case VBOXCMDVBVA_OPTYPE_CRCMD: + { + VBOXCMDVBVA_CRCMD *pCrCmdDr; + VBOXCMDVBVA_CRCMD_CMD *pCrCmd; + int rc; + pCrCmdDr = (VBOXCMDVBVA_CRCMD*)pCmd; + pCrCmd = &pCrCmdDr->Cmd; + if (cbCmd < sizeof (VBOXCMDVBVA_CRCMD)) + { + WARN(("invalid buffer size")); + pCmd->u.i8Result = -1; + break; + } + rc = crVBoxServerCmdVbvaCrCmdProcess(pCrCmd, cbCmd - RT_OFFSETOF(VBOXCMDVBVA_CRCMD, Cmd)); + if (RT_SUCCESS(rc)) + { + /* success */ + pCmd->u.i8Result = 0; + } + else + { + WARN(("crVBoxServerCmdVbvaCrCmdProcess failed, rc %d", rc)); + pCmd->u.i8Result = -1; + } + break; + } + case VBOXCMDVBVA_OPTYPE_BLT_OFFPRIMSZFMT_OR_ID: + { + crVBoxServerCrCmdBltProcess(pCmd, cbCmd); + break; + } + default: + WARN(("unsupported command")); + pCmd->u.i8Result = -1; + } + return VINF_SUCCESS; +} -#ifdef VBOX_WITH_CRHGSMI /* We moved all CrHgsmi command processing to crserverlib to keep the logic of dealing with CrHgsmi commands in one place. * * For now we need the notion of CrHgdmi commands in the crserver_lib to be able to complete it asynchronously once it is really processed. @@ -1452,8 +3392,11 @@ DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t * * NOTE: it is ALWAYS responsibility of the crVBoxServerCrHgsmiCmd to complete the command! * */ + + int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t cbCmd) { + int32_t rc; uint32_t cBuffers = pCmd->cBuffers; uint32_t cParams; @@ -1465,14 +3408,16 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c if (!g_pvVRamBase) { - crWarning("g_pvVRamBase is not initialized"); + WARN(("g_pvVRamBase is not initialized")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_STATE); return VINF_SUCCESS; } if (!cBuffers) { - crWarning("zero buffers passed in!"); + WARN(("zero buffers passed in!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } @@ -1483,14 +3428,16 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); if (!pHdr) { - crWarning("invalid header buffer!"); + WARN(("invalid header buffer!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } if (cbHdr < sizeof (*pHdr)) { - crWarning("invalid header buffer size!"); + WARN(("invalid header buffer size!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } @@ -1502,7 +3449,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c { case SHCRGL_GUEST_FN_WRITE: { - crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); /* @todo: Verify */ if (cParams == 1) @@ -1540,7 +3487,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr); + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1556,7 +3503,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_INJECT: { - crDebug(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); /* @todo: Verify */ if (cParams == 1) @@ -1595,7 +3542,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr); + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1608,7 +3555,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_READ: { - crDebug(("svcCall: SHCRGL_GUEST_FN_READ\n")); + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); /* @todo: Verify */ if (cParams == 1) @@ -1651,6 +3598,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c /* the read command is never pended, complete it right away */ pHdr->result = rc; + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); return VINF_SUCCESS; } @@ -1662,7 +3610,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_WRITE_READ: { - crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); /* @todo: Verify */ if (cParams == 2) @@ -1715,7 +3663,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback); + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1752,8 +3700,24 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c /* we can be on fail only here */ CRASSERT(RT_FAILURE(rc)); pHdr->result = rc; + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); return rc; + +} + +static DECLCALLBACK(bool) crVBoxServerHasData() +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + for (; + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + if (CrFbHas3DData(hFb)) + return true; + } + + return false; } int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t cbCtl) @@ -1767,6 +3731,14 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)pCtl; g_pvVRamBase = (uint8_t*)pSetup->pvVRamBase; g_cbVRam = pSetup->cbVRam; + pSetup->CrCmdServerInfo.hSvr = NULL; + pSetup->CrCmdServerInfo.pfnEnable = crVBoxCrCmdEnable; + pSetup->CrCmdServerInfo.pfnDisable = crVBoxCrCmdDisable; + pSetup->CrCmdServerInfo.pfnCmd = crVBoxCrCmdCmd; + pSetup->CrCmdServerInfo.pfnHostCtl = crVBoxCrCmdHostCtl; + pSetup->CrCmdServerInfo.pfnGuestCtl = crVBoxCrCmdGuestCtl; + pSetup->CrCmdServerInfo.pfnSaveState = crVBoxCrCmdSaveState; + pSetup->CrCmdServerInfo.pfnLoadState = crVBoxCrCmdLoadState; rc = VINF_SUCCESS; break; } @@ -1774,11 +3746,14 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END: rc = VINF_SUCCESS; break; - case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION: + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB: { - PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION)pCtl; + PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB)pCtl; g_hCrHgsmiCompletion = pSetup->hCompletion; g_pfnCrHgsmiCompletion = pSetup->pfnCompletion; + + pSetup->MainInterface.pfnHasData = crVBoxServerHasData; + rc = VINF_SUCCESS; break; } @@ -1795,4 +3770,40 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c * or Hgcm Host Fast Call commands that do require completion. All this details are hidden here */ return rc; } + +int32_t crVBoxServerHgcmEnable(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hRHCmd, PFNVBOXCRCMDCTL_REMAINING_HOST_COMMAND pfnRHCmd) +{ + int rc = VINF_SUCCESS; + uint8_t* pCtl; + uint32_t cbCtl; + + if (cr_server.numClients) + { + WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients)); + return VERR_INVALID_STATE; + } + + for (pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc); pCtl; pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc)) + { + rc = crVBoxCrCmdHostCtl(NULL, pCtl, cbCtl); + } + + crVBoxServerDefaultContextSet(); + + return VINF_SUCCESS; +} + +int32_t crVBoxServerHgcmDisable() +{ + if (cr_server.numClients) + { + WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients)); + return VERR_INVALID_STATE; + } + + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + return VINF_SUCCESS; +} + #endif |
