static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2) { CRContext *pContext = (CRContext *) data1; PSSMHANDLE pSSM = (PSSMHANDLE) data2; int32_t rc; CRASSERT(pContext && pSSM); /* 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); #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE if (cr_server.curClient) { unsigned long id; if (!crHashtableGetDataKey(cr_server.contextTable, pContext, &id)) { crWarning("No client id for server ctx %d", pContext->id); } else { crServerDispatchMakeCurrent(cr_server.curClient->currentWindow, 0, id); } } #endif rc = crStateSaveContext(pContext, pSSM); CRASSERT(rc == VINF_SUCCESS); }
DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h, uint64_t winID) { crDebug("crVBoxServerMapScreen(%i) [%i,%i:%u,%u %x]", sIndex, x, y, w, h, winID); if (sIndex<0 || sIndex>=cr_server.screenCount) return VERR_INVALID_PARAMETER; if (MAPPED(SCREEN(sIndex)) && SCREEN(sIndex).winID!=winID) { crDebug("Mapped screen[%i] is being remapped.", sIndex); crVBoxServerUnmapScreen(sIndex); } SCREEN(sIndex).winID = winID; SCREEN(sIndex).x = x; SCREEN(sIndex).y = y; SCREEN(sIndex).w = w; SCREEN(sIndex).h = h; renderspuSetWindowId(SCREEN(sIndex).winID); crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); renderspuSetWindowId(SCREEN(0).winID); crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL); #ifndef WINDOWS /*Restore FB content for clients, which have current window on a screen being remapped*/ { GLint i; for (i = 0; i < cr_server.numClients; i++) { cr_server.curClient = cr_server.clients[i]; if (cr_server.curClient->currentCtx && (cr_server.curClient->currentCtx->buffer.pFrontImg || cr_server.curClient->currentCtx->buffer.pBackImg) && cr_server.curClient->currentMural && cr_server.curClient->currentMural->screenId == sIndex && cr_server.curClient->currentCtx->buffer.storedHeight == h && cr_server.curClient->currentCtx->buffer.storedWidth == w) { int clientWindow = cr_server.curClient->currentWindow; int clientContext = cr_server.curClient->currentContextNumber; if (clientWindow && clientWindow != cr_server.currentWindow) { crServerDispatchMakeCurrent(clientWindow, 0, clientContext); } crStateApplyFBImage(cr_server.curClient->currentCtx); } } cr_server.curClient = NULL; } #endif return VINF_SUCCESS; }
/** * Process incoming/pending message for the given client (queue entry). * \return CLIENT_GONE if this client has gone away/exited, * CLIENT_NEXT if we can advance to the next client * CLIENT_MORE if we have to process more messages for this client. */ static ClientStatus crServerServiceClient(const RunQueue *qEntry) { CRMessage *msg; CRConnection *conn; /* set current client pointer */ cr_server.curClient = qEntry->client; conn = cr_server.run_queue->client->conn; /* service current client as long as we can */ while (conn && conn->type != CR_NO_CONNECTION && crNetNumMessages(conn) > 0) { unsigned int len; /* crDebug("%d messages on %p", crNetNumMessages(conn), (void *) conn); */ /* Don't use GetMessage, because we want to do our own crNetRecv() calls * here ourself. * Note that crNetPeekMessage() DOES remove the message from the queue * if there is one. */ len = crNetPeekMessage( conn, &msg ); CRASSERT(len > 0); if (msg->header.type != CR_MESSAGE_OPCODES && msg->header.type != CR_MESSAGE_REDIR_PTR) { crError( "SPU %d sent me CRAP (type=0x%x)", cr_server.curClient->spu_id, msg->header.type ); } /* Do the context switch here. No sense in switching before we * really have any work to process. This is a no-op if we're * not really switching contexts. * * XXX This isn't entirely sound. The crStateMakeCurrent() call * will compute the state difference and dispatch it using * the head SPU's dispatch table. * * This is a problem if this is the first buffer coming in, * and the head SPU hasn't had a chance to do a MakeCurrent() * yet (likely because the MakeCurrent() command is in the * buffer itself). * * At best, in this case, the functions are no-ops, and * are essentially ignored by the SPU. In the typical * case, things aren't too bad; if the SPU just calls * crState*() functions to update local state, everything * will work just fine. * * In the worst (but unusual) case where a nontrivial * SPU is at the head of a crserver's SPU chain (say, * in a multiple-tiered "tilesort" arrangement, as * seen in the "multitilesort.conf" configuration), the * SPU may rely on state set during the MakeCurrent() that * may not be present yet, because no MakeCurrent() has * yet been dispatched. * * This headache will have to be revisited in the future; * for now, SPUs that could head a crserver's SPU chain * will have to detect the case that their functions are * being called outside of a MakeCurrent(), and will have * to handle the situation gracefully. (This is currently * the case with the "tilesort" SPU.) */ #if 0 crStateMakeCurrent( cr_server.curClient->currentCtx ); #else /* Check if the current window is the one that the client wants to * draw into. If not, dispatch a MakeCurrent to activate the proper * window. */ if (cr_server.curClient) { int clientWindow = cr_server.curClient->currentWindow; int clientContext = cr_server.curClient->currentContextNumber; CRContextInfo *clientCtxInfo = cr_server.curClient->currentCtxInfo; if (clientCtxInfo != cr_server.currentCtxInfo || clientWindow != cr_server.currentWindow || cr_server.bForceMakeCurrentOnClientSwitch) { crServerDispatchMakeCurrent(clientWindow, 0, clientContext); /* CRASSERT(cr_server.currentWindow == clientWindow); */ } } #endif /* Force scissor, viewport and projection matrix update in * crServerSetOutputBounds(). */ cr_server.currentSerialNo = 0; /* Commands get dispatched here */ crServerDispatchMessage( conn, msg ); crNetFree( conn, msg ); if (qEntry->blocked) { /* Note/assert: we should not be inside a glBegin/End or glNewList/ * glEndList pair at this time! */ CRASSERT(0); return CLIENT_NEXT; } } /* while */ /* * Check if client/connection is gone */ if (!conn || conn->type == CR_NO_CONNECTION) { crDebug("Delete client %p at %d", cr_server.run_queue->client, __LINE__); crServerDeleteClient( cr_server.run_queue->client ); return CLIENT_GONE; } /* * Determine if we can advance to next client. * If we're currently inside a glBegin/End primitive or building a display * list we can't service another client until we're done with the * primitive/list. */ if (crServerClientInBeginEnd(cr_server.curClient)) { /* The next message has to come from the current client's connection. */ CRASSERT(!qEntry->blocked); return CLIENT_MORE; } else { /* get next client */ return CLIENT_NEXT; } }
DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) { int32_t rc, i; uint32_t ui, uiNumElems; unsigned long key; if (!cr_server.bIsInLoadingState) { /* AssertRCReturn(...) will leave us in loading state, but it doesn't matter as we'd be failing anyway */ cr_server.bIsInLoadingState = GL_TRUE; /* Read number of clients */ rc = SSMR3GetU32(pSSM, &g_hackVBoxServerSaveLoadCallsLeft); AssertRCReturn(rc, rc); } g_hackVBoxServerSaveLoadCallsLeft--; /* Do nothing until we're being called last time */ if (g_hackVBoxServerSaveLoadCallsLeft>0) { return VINF_SUCCESS; } if (version!=SHCROGL_SSM_VERSION) { return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; } /* Load and recreate rendering contexts */ rc = SSMR3GetU32(pSSM, &uiNumElems); AssertRCReturn(rc, rc); for (ui=0; ui<uiNumElems; ++ui) { CRCreateInfo_t createInfo; char psz[200]; GLint ctxID; CRContext* pContext; rc = SSMR3GetMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo)); AssertRCReturn(rc, rc); if (createInfo.pszDpyName) { rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL); AssertRCReturn(rc, rc); createInfo.pszDpyName = psz; } ctxID = crServerDispatchCreateContextEx(createInfo.pszDpyName, createInfo.visualBits, 0, key, createInfo.internalID); CRASSERT((int64_t)ctxID == (int64_t)key); pContext = (CRContext*) crHashtableSearch(cr_server.contextTable, key); CRASSERT(pContext); pContext->shared->id=-1; } /* Restore context state data */ for (ui=0; ui<uiNumElems; ++ui) { CRContext *pContext; rc = SSMR3GetMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); pContext = (CRContext*) crHashtableSearch(cr_server.contextTable, key); CRASSERT(pContext); rc = crStateLoadContext(pContext, cr_server.contextTable, pSSM); 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) { rc = SSMR3GetStrZEx(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(pSSM, &uiNumElems); AssertRCReturn(rc, rc); for (ui=0; ui<uiNumElems; ++ui) { CRMuralInfo muralInfo; rc = SSMR3GetMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); rc = SSMR3GetMem(pSSM, &muralInfo, sizeof(muralInfo)); AssertRCReturn(rc, rc); if (muralInfo.pVisibleRects) { muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); if (!muralInfo.pVisibleRects) { return VERR_NO_MEMORY; } rc = SSMR3GetMem(pSSM, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); 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); } crServerDispatchWindowShow(key, muralInfo.bVisible); if (muralInfo.pVisibleRects) { crFree(muralInfo.pVisibleRects); } } /* Load starting free context and window IDs */ rc = SSMR3GetMem(pSSM, &cr_server.idsPool, sizeof(cr_server.idsPool)); CRASSERT(rc == VINF_SUCCESS); /* Load clients info */ for (i = 0; i < cr_server.numClients; i++) { if (cr_server.clients[i] && cr_server.clients[i]->conn) { CRClient *pClient = cr_server.clients[i]; CRClient client; unsigned long ctxID=-1, winID=-1; rc = SSMR3GetU32(pSSM, &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); AssertRCReturn(rc, rc); rc = SSMR3GetU32(pSSM, &pClient->conn->vMinor); AssertRCReturn(rc, rc); } rc = SSMR3GetMem(pSSM, &client, sizeof(client)); CRASSERT(rc == VINF_SUCCESS); client.conn = pClient->conn; /* We can't reassign client number, as we'd get wrong results in TranslateTextureID * and fail to bind old textures. */ /*client.number = pClient->number;*/ *pClient = client; pClient->currentContextNumber = -1; pClient->currentCtx = cr_server.DummyContext; pClient->currentMural = NULL; pClient->currentWindow = -1; cr_server.curClient = pClient; if (client.currentCtx && client.currentContextNumber>=0) { rc = SSMR3GetMem(pSSM, &ctxID, sizeof(ctxID)); AssertRCReturn(rc, rc); client.currentCtx = (CRContext*) crHashtableSearch(cr_server.contextTable, ctxID); CRASSERT(client.currentCtx); //pClient->currentCtx = client.currentCtx; //pClient->currentContextNumber = ctxID; } if (client.currentMural && client.currentWindow>=0) { rc = SSMR3GetMem(pSSM, &winID, sizeof(winID)); AssertRCReturn(rc, rc); client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID); CRASSERT(client.currentMural); //pClient->currentMural = client.currentMural; //pClient->currentWindow = winID; } /* Restore client active context and window */ crServerDispatchMakeCurrent(winID, 0, ctxID); if (0) { CRContext *tmpCtx; CRCreateInfo_t *createInfo; GLfloat one[4] = { 1, 1, 1, 1 }; GLfloat amb[4] = { 0.4f, 0.4f, 0.4f, 1.0f }; crServerDispatchMakeCurrent(winID, 0, ctxID); crHashtableWalk(client.currentCtx->shared->textureTable, crVBoxServerSyncTextureCB, client.currentCtx); crStateTextureObjectDiff(client.currentCtx, NULL, NULL, &client.currentCtx->texture.base1D, GL_TRUE); crStateTextureObjectDiff(client.currentCtx, NULL, NULL, &client.currentCtx->texture.base2D, GL_TRUE); crStateTextureObjectDiff(client.currentCtx, NULL, NULL, &client.currentCtx->texture.base3D, GL_TRUE); #ifdef CR_ARB_texture_cube_map crStateTextureObjectDiff(client.currentCtx, NULL, NULL, &client.currentCtx->texture.baseCubeMap, GL_TRUE); #endif #ifdef CR_NV_texture_rectangle //@todo this doesn't work as expected //crStateTextureObjectDiff(client.currentCtx, NULL, NULL, &client.currentCtx->texture.baseRect, GL_TRUE); #endif /*cr_server.head_spu->dispatch_table.Materialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb); cr_server.head_spu->dispatch_table.LightModelfv(GL_LIGHT_MODEL_AMBIENT, amb); cr_server.head_spu->dispatch_table.Lightfv(GL_LIGHT1, GL_DIFFUSE, one); cr_server.head_spu->dispatch_table.Enable(GL_LIGHTING); cr_server.head_spu->dispatch_table.Enable(GL_LIGHT0); cr_server.head_spu->dispatch_table.Enable(GL_LIGHT1); cr_server.head_spu->dispatch_table.Enable(GL_CULL_FACE); cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_2D);*/ //crStateViewport( 0, 0, 600, 600 ); //pClient->currentMural->viewportValidated = GL_FALSE; //cr_server.head_spu->dispatch_table.Viewport( 0, 0, 600, 600 ); //crStateMatrixMode(GL_PROJECTION); //cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION); //crStateLoadIdentity(); //cr_server.head_spu->dispatch_table.LoadIdentity(); //crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0); //cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0); //crStateMatrixMode(GL_MODELVIEW); //cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW); //crServerDispatchLoadIdentity(); //crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0); //cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0); //crServerDispatchLoadIdentity(); /*createInfo = (CRCreateInfo_t *) crHashtableSearch(cr_server.pContextCreateInfoTable, ctxID); CRASSERT(createInfo); tmpCtx = crStateCreateContext(NULL, createInfo->visualBits, NULL); CRASSERT(tmpCtx); crStateDiffContext(tmpCtx, client.currentCtx); crStateDestroyContext(tmpCtx);*/ } } } //crServerDispatchMakeCurrent(-1, 0, -1); cr_server.curClient = NULL; { GLenum err = crServerDispatchGetError(); if (err != GL_NO_ERROR) { crWarning("crServer: glGetError %d after loading snapshot", err); } } cr_server.bIsInLoadingState = GL_FALSE; return VINF_SUCCESS; }
DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) { int32_t rc, i; uint32_t ui32; GLboolean b; unsigned long key; #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE unsigned long ctxID=-1, winID=-1; #endif /* We shouldn't be called if there's no clients at all*/ CRASSERT(cr_server.numClients>0); /* @todo it's hack atm */ /* We want to be called only once to save server state but atm we're being called from svcSaveState * for every connected client (e.g. guest opengl application) */ if (!cr_server.bIsInSavingState) /* It's first call */ { cr_server.bIsInSavingState = GL_TRUE; /* Store number of clients */ rc = SSMR3PutU32(pSSM, (uint32_t) cr_server.numClients); AssertRCReturn(rc, rc); g_hackVBoxServerSaveLoadCallsLeft = cr_server.numClients; } g_hackVBoxServerSaveLoadCallsLeft--; /* Do nothing until we're being called last time */ if (g_hackVBoxServerSaveLoadCallsLeft>0) { return VINF_SUCCESS; } /* Save rendering contexts creation info */ ui32 = crHashtableNumElements(cr_server.pContextCreateInfoTable); rc = SSMR3PutU32(pSSM, (uint32_t) ui32); AssertRCReturn(rc, rc); crHashtableWalk(cr_server.pContextCreateInfoTable, crVBoxServerSaveCreateInfoCB, pSSM); #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */ if (cr_server.curClient) { ctxID = cr_server.curClient->currentContextNumber; winID = cr_server.curClient->currentWindow; } #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) { crServerDispatchMakeCurrent(winID, 0, ctxID); } #endif /* Save windows creation info */ ui32 = crHashtableNumElements(cr_server.pWindowCreateInfoTable); rc = SSMR3PutU32(pSSM, (uint32_t) ui32); AssertRCReturn(rc, rc); crHashtableWalk(cr_server.pWindowCreateInfoTable, crVBoxServerSaveCreateInfoCB, 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)); AssertRCReturn(rc, rc); /* Save clients info */ for (i = 0; i < cr_server.numClients; i++) { if (cr_server.clients[i] && cr_server.clients[i]->conn) { CRClient *pClient = cr_server.clients[i]; rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID); AssertRCReturn(rc, rc); rc = SSMR3PutU32(pSSM, pClient->conn->vMajor); AssertRCReturn(rc, rc); rc = SSMR3PutU32(pSSM, pClient->conn->vMinor); AssertRCReturn(rc, rc); rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient)); AssertRCReturn(rc, rc); if (pClient->currentCtx && pClient->currentContextNumber>=0) { b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtx, &key); CRASSERT(b); rc = SSMR3PutMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); } if (pClient->currentMural && pClient->currentWindow>=0) { b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key); CRASSERT(b); rc = SSMR3PutMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); } } } cr_server.bIsInSavingState = GL_FALSE; return VINF_SUCCESS; }