VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c@ 45202

Last change on this file since 45202 was 45202, checked in by vboxsync, 12 years ago

crOpenGL: proper blitter cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 92.5 KB
Line 
1/* Copyright (c) 2001, Stanford University
2 * All rights reserved
3 *
4 * See the file LICENSE.txt for information on redistributing this software.
5 */
6
7#include "server.h"
8#include "cr_net.h"
9#include "cr_unpack.h"
10#include "cr_error.h"
11#include "cr_glstate.h"
12#include "cr_string.h"
13#include "cr_mem.h"
14#include "cr_hash.h"
15#include "cr_vreg.h"
16#include "cr_environment.h"
17#include "cr_pixeldata.h"
18#include "server_dispatch.h"
19#include "state/cr_texture.h"
20#include "render/renderspu.h"
21#include <signal.h>
22#include <stdlib.h>
23#define DEBUG_FP_EXCEPTIONS 0
24#if DEBUG_FP_EXCEPTIONS
25#include <fpu_control.h>
26#include <math.h>
27#endif
28#include <iprt/assert.h>
29#include <VBox/err.h>
30
31#ifdef VBOXCR_LOGFPS
32#include <iprt/timer.h>
33#endif
34
35#ifdef VBOX_WITH_CRHGSMI
36# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
37uint8_t* g_pvVRamBase = NULL;
38uint32_t g_cbVRam = 0;
39HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion = NULL;
40PFNCRHGSMICMDCOMPLETION g_pfnCrHgsmiCompletion = NULL;
41#endif
42
43/**
44 * \mainpage CrServerLib
45 *
46 * \section CrServerLibIntroduction Introduction
47 *
48 * Chromium consists of all the top-level files in the cr
49 * directory. The core module basically takes care of API dispatch,
50 * and OpenGL state management.
51 */
52
53
54/**
55 * CRServer global data
56 */
57CRServer cr_server;
58
59int tearingdown = 0; /* can't be static */
60
61DECLINLINE(int32_t) crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient)
62{
63 CRClient *pClient = NULL;
64 int32_t i;
65
66 *ppClient = NULL;
67
68 for (i = 0; i < cr_server.numClients; i++)
69 {
70 if (cr_server.clients[i] && cr_server.clients[i]->conn
71 && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
72 {
73 pClient = cr_server.clients[i];
74 break;
75 }
76 }
77 if (!pClient)
78 {
79 crWarning("client not found!");
80 return VERR_INVALID_PARAMETER;
81 }
82
83 if (!pClient->conn->vMajor)
84 {
85 crWarning("no major version specified for client!");
86 return VERR_NOT_SUPPORTED;
87 }
88
89 *ppClient = pClient;
90
91 return VINF_SUCCESS;
92}
93
94
95/**
96 * Return pointer to server's first SPU.
97 */
98SPU*
99crServerHeadSPU(void)
100{
101 return cr_server.head_spu;
102}
103
104
105
106static void DeleteBarrierCallback( void *data )
107{
108 CRServerBarrier *barrier = (CRServerBarrier *) data;
109 crFree(barrier->waiting);
110 crFree(barrier);
111}
112
113
114static void deleteContextInfoCallback( void *data )
115{
116 CRContextInfo *c = (CRContextInfo *) data;
117 crStateDestroyContext(c->pContext);
118 if (c->CreateInfo.pszDpyName)
119 crFree(c->CreateInfo.pszDpyName);
120 crFree(c);
121}
122
123static void deleteMuralInfoCallback( void *data )
124{
125 CRMuralInfo *m = (CRMuralInfo *) data;
126 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,
127 * and renderspu will destroy it up itself*/
128 {
129 crServerMuralTerm(m);
130 }
131 crFree(m);
132}
133
134static void crServerTearDown( void )
135{
136 GLint i;
137 CRClientNode *pNode, *pNext;
138
139 /* avoid a race condition */
140 if (tearingdown)
141 return;
142
143 tearingdown = 1;
144
145 crStateSetCurrent( NULL );
146
147 cr_server.curClient = NULL;
148 cr_server.run_queue = NULL;
149
150 crFree( cr_server.overlap_intens );
151 cr_server.overlap_intens = NULL;
152
153 /* needed to make sure window dummy mural not get created on mural destruction
154 * and generally this should be zeroed up */
155 cr_server.currentCtxInfo = NULL;
156 cr_server.currentWindow = 0;
157 cr_server.currentNativeWindow = 0;
158 cr_server.currentMural = NULL;
159
160 if (CrBltIsInitialized(&cr_server.Blitter))
161 {
162 CrBltTerm(&cr_server.Blitter);
163 }
164
165 /* sync our state with renderspu,
166 * do it before mural & context deletion to avoid deleting currently set murals/contexts*/
167 cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0);
168
169 /* Deallocate all semaphores */
170 crFreeHashtable(cr_server.semaphores, crFree);
171 cr_server.semaphores = NULL;
172
173 /* Deallocate all barriers */
174 crFreeHashtable(cr_server.barriers, DeleteBarrierCallback);
175 cr_server.barriers = NULL;
176
177 /* Free all context info */
178 crFreeHashtable(cr_server.contextTable, deleteContextInfoCallback);
179
180 /* Free vertex programs */
181 crFreeHashtable(cr_server.programTable, crFree);
182
183 /* Free dummy murals */
184 crFreeHashtable(cr_server.dummyMuralTable, deleteMuralInfoCallback);
185
186 /* Free murals */
187 crFreeHashtable(cr_server.muralTable, deleteMuralInfoCallback);
188
189 for (i = 0; i < cr_server.numClients; i++) {
190 if (cr_server.clients[i]) {
191 CRConnection *conn = cr_server.clients[i]->conn;
192 crNetFreeConnection(conn);
193 crFree(cr_server.clients[i]);
194 }
195 }
196 cr_server.numClients = 0;
197
198 pNode = cr_server.pCleanupClient;
199 while (pNode)
200 {
201 pNext=pNode->next;
202 crFree(pNode->pClient);
203 crFree(pNode);
204 pNode=pNext;
205 }
206 cr_server.pCleanupClient = NULL;
207
208#if 1
209 /* disable these two lines if trying to get stack traces with valgrind */
210 crSPUUnloadChain(cr_server.head_spu);
211 cr_server.head_spu = NULL;
212#endif
213
214 crStateDestroy();
215
216 crNetTearDown();
217
218 VBoxVrListClear(&cr_server.RootVr);
219
220 VBoxVrTerm();
221}
222
223static void crServerClose( unsigned int id )
224{
225 crError( "Client disconnected!" );
226 (void) id;
227}
228
229static void crServerCleanup( int sigio )
230{
231 crServerTearDown();
232
233 tearingdown = 0;
234}
235
236
237void
238crServerSetPort(int port)
239{
240 cr_server.tcpip_port = port;
241}
242
243
244
245static void
246crPrintHelp(void)
247{
248 printf("Usage: crserver [OPTIONS]\n");
249 printf("Options:\n");
250 printf(" -mothership URL Specifies URL for contacting the mothership.\n");
251 printf(" URL is of the form [protocol://]hostname[:port]\n");
252 printf(" -port N Specifies the port number this server will listen to.\n");
253 printf(" -help Prints this information.\n");
254}
255
256
257/**
258 * Do CRServer initializations. After this, we can begin servicing clients.
259 */
260void
261crServerInit(int argc, char *argv[])
262{
263 int i;
264 char *mothership = NULL;
265 CRMuralInfo *defaultMural;
266 int rc = VBoxVrInit();
267 if (!RT_SUCCESS(rc))
268 {
269 crWarning("VBoxVrInit failed, rc %d", rc);
270 return;
271 }
272
273 for (i = 1 ; i < argc ; i++)
274 {
275 if (!crStrcmp( argv[i], "-mothership" ))
276 {
277 if (i == argc - 1)
278 {
279 crError( "-mothership requires an argument" );
280 }
281 mothership = argv[i+1];
282 i++;
283 }
284 else if (!crStrcmp( argv[i], "-port" ))
285 {
286 /* This is the port on which we'll accept client connections */
287 if (i == argc - 1)
288 {
289 crError( "-port requires an argument" );
290 }
291 cr_server.tcpip_port = crStrToInt(argv[i+1]);
292 i++;
293 }
294 else if (!crStrcmp( argv[i], "-vncmode" ))
295 {
296 cr_server.vncMode = 1;
297 }
298 else if (!crStrcmp( argv[i], "-help" ))
299 {
300 crPrintHelp();
301 exit(0);
302 }
303 }
304
305 signal( SIGTERM, crServerCleanup );
306 signal( SIGINT, crServerCleanup );
307#ifndef WINDOWS
308 signal( SIGPIPE, SIG_IGN );
309#endif
310
311#if DEBUG_FP_EXCEPTIONS
312 {
313 fpu_control_t mask;
314 _FPU_GETCW(mask);
315 mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM
316 | _FPU_MASK_OM | _FPU_MASK_UM);
317 _FPU_SETCW(mask);
318 }
319#endif
320
321 cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL);
322
323 if (cr_server.bUseMultipleContexts)
324 {
325 crInfo("Info: using multiple contexts!");
326 crDebug("Debug: using multiple contexts!");
327 }
328
329 cr_server.firstCallCreateContext = GL_TRUE;
330 cr_server.firstCallMakeCurrent = GL_TRUE;
331 cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
332
333 /*
334 * Create default mural info and hash table.
335 */
336 cr_server.muralTable = crAllocHashtable();
337 defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
338 defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID;
339 crHashtableAdd(cr_server.muralTable, 0, defaultMural);
340
341 cr_server.programTable = crAllocHashtable();
342
343 crNetInit(crServerRecv, crServerClose);
344 crStateInit();
345
346 crServerSetVBoxConfiguration();
347
348 crStateLimitsInit( &(cr_server.limits) );
349
350 /*
351 * Default context
352 */
353 cr_server.contextTable = crAllocHashtable();
354 cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo;
355
356 cr_server.dummyMuralTable = crAllocHashtable();
357
358 cr_server.fRootVrOn = GL_FALSE;
359 VBoxVrListInit(&cr_server.RootVr);
360 crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
361
362 crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter));
363
364 crServerInitDispatch();
365 crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
366
367 crUnpackSetReturnPointer( &(cr_server.return_ptr) );
368 crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) );
369
370 cr_server.barriers = crAllocHashtable();
371 cr_server.semaphores = crAllocHashtable();
372}
373
374void crVBoxServerTearDown(void)
375{
376 crServerTearDown();
377}
378
379/**
380 * Do CRServer initializations. After this, we can begin servicing clients.
381 */
382GLboolean crVBoxServerInit(void)
383{
384 CRMuralInfo *defaultMural;
385
386 int rc = VBoxVrInit();
387 if (!RT_SUCCESS(rc))
388 {
389 crWarning("VBoxVrInit failed, rc %d", rc);
390 return GL_FALSE;
391 }
392
393#if DEBUG_FP_EXCEPTIONS
394 {
395 fpu_control_t mask;
396 _FPU_GETCW(mask);
397 mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM
398 | _FPU_MASK_OM | _FPU_MASK_UM);
399 _FPU_SETCW(mask);
400 }
401#endif
402
403 cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL);
404
405 if (cr_server.bUseMultipleContexts)
406 {
407 crInfo("Info: using multiple contexts!");
408 crDebug("Debug: using multiple contexts!");
409 }
410
411 crNetInit(crServerRecv, crServerClose);
412
413 cr_server.firstCallCreateContext = GL_TRUE;
414 cr_server.firstCallMakeCurrent = GL_TRUE;
415
416 cr_server.bIsInLoadingState = GL_FALSE;
417 cr_server.bIsInSavingState = GL_FALSE;
418 cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE;
419
420 cr_server.pCleanupClient = NULL;
421
422 /*
423 * Create default mural info and hash table.
424 */
425 cr_server.muralTable = crAllocHashtable();
426 defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
427 defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID;
428 crHashtableAdd(cr_server.muralTable, 0, defaultMural);
429
430 cr_server.programTable = crAllocHashtable();
431
432 crStateInit();
433
434 crStateLimitsInit( &(cr_server.limits) );
435
436 cr_server.barriers = crAllocHashtable();
437 cr_server.semaphores = crAllocHashtable();
438
439 crUnpackSetReturnPointer( &(cr_server.return_ptr) );
440 crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) );
441
442 /*
443 * Default context
444 */
445 cr_server.contextTable = crAllocHashtable();
446
447 cr_server.dummyMuralTable = crAllocHashtable();
448
449 cr_server.fRootVrOn = GL_FALSE;
450 VBoxVrListInit(&cr_server.RootVr);
451 crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
452
453 crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter));
454
455 crServerSetVBoxConfigurationHGCM();
456
457 if (!cr_server.head_spu)
458 return GL_FALSE;
459
460 crServerInitDispatch();
461 crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
462
463 /*Check for PBO support*/
464 if (crStateGetCurrent()->extensions.ARB_pixel_buffer_object)
465 {
466 cr_server.bUsePBOForReadback=GL_TRUE;
467 }
468
469 return GL_TRUE;
470}
471
472int32_t crVBoxServerAddClient(uint32_t u32ClientID)
473{
474 CRClient *newClient;
475
476 if (cr_server.numClients>=CR_MAX_CLIENTS)
477 {
478 return VERR_MAX_THRDS_REACHED;
479 }
480
481 newClient = (CRClient *) crCalloc(sizeof(CRClient));
482 crDebug("crServer: AddClient u32ClientID=%d", u32ClientID);
483
484 newClient->spu_id = 0;
485 newClient->currentCtxInfo = &cr_server.MainContextInfo;
486 newClient->currentContextNumber = -1;
487 newClient->conn = crNetAcceptClient(cr_server.protocol, NULL,
488 cr_server.tcpip_port,
489 cr_server.mtu, 0);
490 newClient->conn->u32ClientID = u32ClientID;
491
492 cr_server.clients[cr_server.numClients++] = newClient;
493
494 crServerAddToRunQueue(newClient);
495
496 return VINF_SUCCESS;
497}
498
499void crVBoxServerRemoveClient(uint32_t u32ClientID)
500{
501 CRClient *pClient=NULL;
502 int32_t i;
503
504 crDebug("crServer: RemoveClient u32ClientID=%d", u32ClientID);
505
506 for (i = 0; i < cr_server.numClients; i++)
507 {
508 if (cr_server.clients[i] && cr_server.clients[i]->conn
509 && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
510 {
511 pClient = cr_server.clients[i];
512 break;
513 }
514 }
515 //if (!pClient) return VERR_INVALID_PARAMETER;
516 if (!pClient)
517 {
518 crWarning("Invalid client id %u passed to crVBoxServerRemoveClient", u32ClientID);
519 return;
520 }
521
522#ifdef VBOX_WITH_CRHGSMI
523 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
524#endif
525
526 /* Disconnect the client */
527 pClient->conn->Disconnect(pClient->conn);
528
529 /* Let server clear client from the queue */
530 crServerDeleteClient(pClient);
531}
532
533static int32_t crVBoxServerInternalClientWriteRead(CRClient *pClient)
534{
535#ifdef VBOXCR_LOGFPS
536 uint64_t tstart, tend;
537#endif
538
539 /*crDebug("=>crServer: ClientWrite u32ClientID=%d", u32ClientID);*/
540
541
542#ifdef VBOXCR_LOGFPS
543 tstart = RTTimeNanoTS();
544#endif
545
546 /* This should be setup already */
547 CRASSERT(pClient->conn->pBuffer);
548 CRASSERT(pClient->conn->cbBuffer);
549#ifdef VBOX_WITH_CRHGSMI
550 CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(&pClient->conn->CmdData);
551#endif
552
553 if (
554#ifdef VBOX_WITH_CRHGSMI
555 !CRVBOXHGSMI_CMDDATA_IS_SET(&pClient->conn->CmdData) &&
556#endif
557 cr_server.run_queue->client != pClient
558 && crServerClientInBeginEnd(cr_server.run_queue->client))
559 {
560 crDebug("crServer: client %d blocked, allow_redir_ptr = 0", pClient->conn->u32ClientID);
561 pClient->conn->allow_redir_ptr = 0;
562 }
563 else
564 {
565 pClient->conn->allow_redir_ptr = 1;
566 }
567
568 crNetRecv();
569 CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
570 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
571
572 crServerServiceClients();
573
574#if 0
575 if (pClient->currentMural) {
576 crStateViewport( 0, 0, 500, 500 );
577 pClient->currentMural->viewportValidated = GL_FALSE;
578 cr_server.head_spu->dispatch_table.Viewport( 0, 0, 500, 500 );
579 crStateViewport( 0, 0, 600, 600 );
580 pClient->currentMural->viewportValidated = GL_FALSE;
581 cr_server.head_spu->dispatch_table.Viewport( 0, 0, 600, 600 );
582
583 crStateMatrixMode(GL_PROJECTION);
584 cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION);
585 crServerDispatchLoadIdentity();
586 crStateFrustum(-0.6, 0.6, -0.5, 0.5, 1.5, 150.0);
587 cr_server.head_spu->dispatch_table.Frustum(-0.6, 0.6, -0.5, 0.5, 1.5, 150.0);
588 crServerDispatchLoadIdentity();
589 crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
590 cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
591
592 crStateMatrixMode(GL_MODELVIEW);
593 cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
594 crServerDispatchLoadIdentity();
595 crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
596 cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
597 crServerDispatchLoadIdentity();
598 }
599#endif
600
601 crStateResetCurrentPointers(&cr_server.current);
602
603#ifndef VBOX_WITH_CRHGSMI
604 CRASSERT(!pClient->conn->allow_redir_ptr || crNetNumMessages(pClient->conn)==0);
605#endif
606
607#ifdef VBOXCR_LOGFPS
608 tend = RTTimeNanoTS();
609 pClient->timeUsed += tend-tstart;
610#endif
611 /*crDebug("<=crServer: ClientWrite u32ClientID=%d", u32ClientID);*/
612
613 return VINF_SUCCESS;
614}
615
616
617int32_t crVBoxServerClientWrite(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t cbBuffer)
618{
619 CRClient *pClient=NULL;
620 int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient);
621
622 if (RT_FAILURE(rc))
623 return rc;
624
625
626 CRASSERT(pBuffer);
627
628 /* This should never fire unless we start to multithread */
629 CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
630
631 pClient->conn->pBuffer = pBuffer;
632 pClient->conn->cbBuffer = cbBuffer;
633#ifdef VBOX_WITH_CRHGSMI
634 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
635#endif
636
637 return crVBoxServerInternalClientWriteRead(pClient);
638}
639
640int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer)
641{
642 if (pClient->conn->cbHostBuffer > *pcbBuffer)
643 {
644 crDebug("crServer: [%lx] ClientRead u32ClientID=%d FAIL, host buffer too small %d of %d",
645 crThreadID(), pClient->conn->u32ClientID, *pcbBuffer, pClient->conn->cbHostBuffer);
646
647 /* Return the size of needed buffer */
648 *pcbBuffer = pClient->conn->cbHostBuffer;
649
650 return VERR_BUFFER_OVERFLOW;
651 }
652
653 *pcbBuffer = pClient->conn->cbHostBuffer;
654
655 if (*pcbBuffer)
656 {
657 CRASSERT(pClient->conn->pHostBuffer);
658
659 crMemcpy(pBuffer, pClient->conn->pHostBuffer, *pcbBuffer);
660 pClient->conn->cbHostBuffer = 0;
661 }
662
663 return VINF_SUCCESS;
664}
665
666int32_t crVBoxServerClientRead(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t *pcbBuffer)
667{
668 CRClient *pClient=NULL;
669 int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient);
670
671 if (RT_FAILURE(rc))
672 return rc;
673
674#ifdef VBOX_WITH_CRHGSMI
675 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
676#endif
677
678 return crVBoxServerInternalClientRead(pClient, pBuffer, pcbBuffer);
679}
680
681int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint32_t vMinor)
682{
683 CRClient *pClient=NULL;
684 int32_t i;
685
686 for (i = 0; i < cr_server.numClients; i++)
687 {
688 if (cr_server.clients[i] && cr_server.clients[i]->conn
689 && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
690 {
691 pClient = cr_server.clients[i];
692 break;
693 }
694 }
695 if (!pClient) return VERR_INVALID_PARAMETER;
696
697 pClient->conn->vMajor = vMajor;
698 pClient->conn->vMinor = vMinor;
699
700 if (vMajor != CR_PROTOCOL_VERSION_MAJOR
701 || vMinor != CR_PROTOCOL_VERSION_MINOR)
702 {
703 return VERR_NOT_SUPPORTED;
704 }
705 else return VINF_SUCCESS;
706}
707
708int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid)
709{
710 CRClient *pClient=NULL;
711 int32_t i;
712
713 for (i = 0; i < cr_server.numClients; i++)
714 {
715 if (cr_server.clients[i] && cr_server.clients[i]->conn
716 && cr_server.clients[i]->conn->u32ClientID==u32ClientID)
717 {
718 pClient = cr_server.clients[i];
719 break;
720 }
721 }
722 if (!pClient) return VERR_INVALID_PARAMETER;
723
724 pClient->pid = pid;
725
726 return VINF_SUCCESS;
727}
728
729int
730CRServerMain(int argc, char *argv[])
731{
732 crServerInit(argc, argv);
733
734 crServerSerializeRemoteStreams();
735
736 crServerTearDown();
737
738 tearingdown = 0;
739
740 return 0;
741}
742
743static void crVBoxServerSaveMuralCB(unsigned long key, void *data1, void *data2)
744{
745 CRMuralInfo *pMI = (CRMuralInfo*) data1;
746 PSSMHANDLE pSSM = (PSSMHANDLE) data2;
747 int32_t rc;
748
749 CRASSERT(pMI && pSSM);
750
751 /* Don't store default mural */
752 if (!key) return;
753
754 rc = SSMR3PutMem(pSSM, &key, sizeof(key));
755 CRASSERT(rc == VINF_SUCCESS);
756
757 rc = SSMR3PutMem(pSSM, pMI, RT_OFFSETOF(CRMuralInfo, CreateInfo));
758 CRASSERT(rc == VINF_SUCCESS);
759
760 if (pMI->pVisibleRects)
761 {
762 rc = SSMR3PutMem(pSSM, pMI->pVisibleRects, 4*sizeof(GLint)*pMI->cVisibleRects);
763 }
764
765 rc = SSMR3PutMem(pSSM, pMI->ctxUsage, sizeof (pMI->ctxUsage));
766 CRASSERT(rc == VINF_SUCCESS);
767}
768
769/* @todo add hashtable walker with result info and intermediate abort */
770static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *data2)
771{
772 CRCreateInfo_t *pCreateInfo = (CRCreateInfo_t *)data1;
773 PSSMHANDLE pSSM = (PSSMHANDLE) data2;
774 int32_t rc;
775
776 CRASSERT(pCreateInfo && pSSM);
777
778 /* Don't store default mural create info */
779 if (!key) return;
780
781 rc = SSMR3PutMem(pSSM, &key, sizeof(key));
782 CRASSERT(rc == VINF_SUCCESS);
783
784 rc = SSMR3PutMem(pSSM, pCreateInfo, sizeof(*pCreateInfo));
785 CRASSERT(rc == VINF_SUCCESS);
786
787 if (pCreateInfo->pszDpyName)
788 {
789 rc = SSMR3PutStrZ(pSSM, pCreateInfo->pszDpyName);
790 CRASSERT(rc == VINF_SUCCESS);
791 }
792}
793
794static void crVBoxServerSaveCreateInfoFromMuralInfoCB(unsigned long key, void *data1, void *data2)
795{
796 CRMuralInfo *pMural = (CRMuralInfo *)data1;
797 CRCreateInfo_t *pCreateInfo = &pMural->CreateInfo;
798 crVBoxServerSaveCreateInfoCB(key, pCreateInfo, data2);
799}
800
801static void crVBoxServerSaveCreateInfoFromCtxInfoCB(unsigned long key, void *data1, void *data2)
802{
803 CRContextInfo *pContextInfo = (CRContextInfo *)data1;
804 CRCreateInfo_t CreateInfo = pContextInfo->CreateInfo;
805 /* saved state contains internal id */
806 CreateInfo.externalID = pContextInfo->pContext->id;
807 crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2);
808}
809
810static void crVBoxServerSyncTextureCB(unsigned long key, void *data1, void *data2)
811{
812 CRTextureObj *pTexture = (CRTextureObj *) data1;
813 CRContext *pContext = (CRContext *) data2;
814
815 CRASSERT(pTexture && pContext);
816 crStateTextureObjectDiff(pContext, NULL, NULL, pTexture, GL_TRUE);
817}
818
819typedef struct CRVBOX_SAVE_STATE_GLOBAL
820{
821 /* context id -> mural association
822 * on context data save, each context will be made current with the corresponding mural from this table
823 * thus saving the mural front & back buffer data */
824 CRHashTable *contextMuralTable;
825 /* mural id -> context info
826 * for murals that do not have associated context in contextMuralTable
827 * we still need to save*/
828 CRHashTable *additionalMuralContextTable;
829
830 PSSMHANDLE pSSM;
831
832 int rc;
833} CRVBOX_SAVE_STATE_GLOBAL, *PCRVBOX_SAVE_STATE_GLOBAL;
834
835
836typedef struct CRVBOX_CTXWND_CTXWALKER_CB
837{
838 PCRVBOX_SAVE_STATE_GLOBAL pGlobal;
839 CRHashTable *usedMuralTable;
840 GLuint cAdditionalMurals;
841} CRVBOX_CTXWND_CTXWALKER_CB, *PCRVBOX_CTXWND_CTXWALKER_CB;
842
843static void crVBoxServerBuildAdditionalWindowContextMapCB(unsigned long key, void *data1, void *data2)
844{
845 CRMuralInfo * pMural = (CRMuralInfo *) data1;
846 PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
847 CRContextInfo *pContextInfo = NULL;
848
849 if (!pMural->CreateInfo.externalID)
850 {
851 CRASSERT(!key);
852 return;
853 }
854
855 if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID))
856 {
857 Assert(crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL));
858 return;
859 }
860
861 Assert(!crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL));
862
863 if (cr_server.MainContextInfo.CreateInfo.visualBits == pMural->CreateInfo.visualBits)
864 {
865 pContextInfo = &cr_server.MainContextInfo;
866 }
867 else
868 {
869 crWarning("different visual bits not implemented!");
870 pContextInfo = &cr_server.MainContextInfo;
871 }
872
873 crHashtableAdd(pData->pGlobal->additionalMuralContextTable, pMural->CreateInfo.externalID, pContextInfo);
874}
875
876
877typedef struct CRVBOX_CTXWND_WNDWALKER_CB
878{
879 PCRVBOX_SAVE_STATE_GLOBAL pGlobal;
880 CRHashTable *usedMuralTable;
881 CRContextInfo *pContextInfo;
882 CRMuralInfo * pMural;
883} CRVBOX_CTXWND_WNDWALKER_CB, *PCRVBOX_CTXWND_WNDWALKER_CB;
884
885static void crVBoxServerBuildContextWindowMapWindowWalkerCB(unsigned long key, void *data1, void *data2)
886{
887 CRMuralInfo * pMural = (CRMuralInfo *) data1;
888 PCRVBOX_CTXWND_WNDWALKER_CB pData = (PCRVBOX_CTXWND_WNDWALKER_CB)data2;
889
890 Assert(pData->pMural != pMural);
891 Assert(pData->pContextInfo);
892
893 if (pData->pMural)
894 return;
895
896 if (!pMural->CreateInfo.externalID)
897 {
898 CRASSERT(!key);
899 return;
900 }
901
902 if (!CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pData->pContextInfo->pContext))
903 return;
904
905 if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID))
906 return;
907
908 CRASSERT(pMural->CreateInfo.visualBits == pData->pContextInfo->CreateInfo.visualBits);
909 pData->pMural = pMural;
910}
911
912static void crVBoxServerBuildContextUsedWindowMapCB(unsigned long key, void *data1, void *data2)
913{
914 CRContextInfo *pContextInfo = (CRContextInfo *)data1;
915 PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
916
917 if (!pContextInfo->currentMural)
918 return;
919
920 crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pContextInfo->currentMural);
921 crHashtableAdd(pData->usedMuralTable, pContextInfo->currentMural->CreateInfo.externalID, pContextInfo->currentMural);
922}
923
924CRMuralInfo * crServerGetDummyMural(GLint visualBits)
925{
926 CRMuralInfo * pMural = (CRMuralInfo *)crHashtableSearch(cr_server.dummyMuralTable, visualBits);
927 if (!pMural)
928 {
929 GLint id;
930 pMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo));
931 if (!pMural)
932 {
933 crWarning("crCalloc failed!");
934 return NULL;
935 }
936 id = crServerMuralInit(pMural, "", visualBits, -1);
937 if (id < 0)
938 {
939 crWarning("crServerMuralInit failed!");
940 crFree(pMural);
941 return NULL;
942 }
943
944 crHashtableAdd(cr_server.dummyMuralTable, visualBits, pMural);
945 }
946
947 return pMural;
948}
949
950static void crVBoxServerBuildContextUnusedWindowMapCB(unsigned long key, void *data1, void *data2)
951{
952 CRContextInfo *pContextInfo = (CRContextInfo *)data1;
953 PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2;
954 CRMuralInfo * pMural = NULL;
955
956 if (pContextInfo->currentMural)
957 return;
958
959 Assert(crHashtableNumElements(pData->pGlobal->contextMuralTable) <= crHashtableNumElements(cr_server.muralTable) - 1);
960 if (crHashtableNumElements(pData->pGlobal->contextMuralTable) < crHashtableNumElements(cr_server.muralTable) - 1)
961 {
962 CRVBOX_CTXWND_WNDWALKER_CB MuralData;
963 MuralData.pGlobal = pData->pGlobal;
964 MuralData.usedMuralTable = pData->usedMuralTable;
965 MuralData.pContextInfo = pContextInfo;
966 MuralData.pMural = NULL;
967
968 crHashtableWalk(cr_server.muralTable, crVBoxServerBuildContextWindowMapWindowWalkerCB, &MuralData);
969
970 pMural = MuralData.pMural;
971
972 }
973
974 if (!pMural)
975 {
976 pMural = crServerGetDummyMural(pContextInfo->CreateInfo.visualBits);
977 if (!pMural)
978 {
979 crWarning("crServerGetDummyMural failed");
980 return;
981 }
982 }
983 else
984 {
985 crHashtableAdd(pData->usedMuralTable, pMural->CreateInfo.externalID, pMural);
986 ++pData->cAdditionalMurals;
987 }
988
989 crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pMural);
990}
991
992static void crVBoxServerBuildSaveStateGlobal(PCRVBOX_SAVE_STATE_GLOBAL pGlobal)
993{
994 CRVBOX_CTXWND_CTXWALKER_CB Data;
995 GLuint cMurals;
996 pGlobal->contextMuralTable = crAllocHashtable();
997 pGlobal->additionalMuralContextTable = crAllocHashtable();
998 /* 1. go through all contexts and match all having currentMural set */
999 Data.pGlobal = pGlobal;
1000 Data.usedMuralTable = crAllocHashtable();
1001 Data.cAdditionalMurals = 0;
1002 crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUsedWindowMapCB, &Data);
1003
1004 cMurals = crHashtableNumElements(pGlobal->contextMuralTable);
1005 CRASSERT(cMurals <= crHashtableNumElements(cr_server.contextTable));
1006 CRASSERT(cMurals <= crHashtableNumElements(cr_server.muralTable) - 1);
1007 CRASSERT(cMurals == crHashtableNumElements(Data.usedMuralTable));
1008 if (cMurals < crHashtableNumElements(cr_server.contextTable))
1009 {
1010 Data.cAdditionalMurals = 0;
1011 crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUnusedWindowMapCB, &Data);
1012 }
1013
1014 CRASSERT(crHashtableNumElements(pGlobal->contextMuralTable) == crHashtableNumElements(cr_server.contextTable));
1015 CRASSERT(cMurals + Data.cAdditionalMurals <= crHashtableNumElements(cr_server.muralTable) - 1);
1016 if (cMurals + Data.cAdditionalMurals < crHashtableNumElements(cr_server.muralTable) - 1)
1017 {
1018 crHashtableWalk(cr_server.muralTable, crVBoxServerBuildAdditionalWindowContextMapCB, &Data);
1019 CRASSERT(cMurals + Data.cAdditionalMurals + crHashtableNumElements(pGlobal->additionalMuralContextTable) == crHashtableNumElements(cr_server.muralTable) - 1);
1020 }
1021
1022 crFreeHashtable(Data.usedMuralTable, NULL);
1023}
1024
1025static void crVBoxServerFBImageDataTerm(CRFBData *pData)
1026{
1027 GLuint i;
1028 for (i = 0; i < pData->cElements; ++i)
1029 {
1030 CRFBDataElement * pEl = &pData->aElements[i];
1031 if (pEl->pvData)
1032 {
1033 crFree(pEl->pvData);
1034 /* sanity */
1035 pEl->pvData = NULL;
1036 }
1037 }
1038 pData->cElements = 0;
1039}
1040
1041static int crVBoxServerFBImageDataInitEx(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite, uint32_t version, GLuint overrideWidth, GLuint overrideHeight)
1042{
1043 CRContext *pContext;
1044 GLuint i;
1045 GLfloat *pF;
1046 CRFBDataElement *pEl;
1047 GLuint width;
1048 GLuint height;
1049
1050 crMemset(pData, 0, sizeof (*pData));
1051
1052 pContext = pCtxInfo->pContext;
1053
1054 /* the version should be always actual when we do reads,
1055 * i.e. it could differ on writes when snapshot is getting loaded */
1056 CRASSERT(fWrite || version == SHCROGL_SSM_VERSION);
1057
1058 width = overrideWidth ? overrideWidth : pMural->width;
1059 height = overrideHeight ? overrideHeight : pMural->height;
1060
1061 if (!width || !height)
1062 return VINF_SUCCESS;
1063
1064 pData->idFBO = pMural->fUseFBO ? pMural->aidColorTexs[fWrite ? pMural->iCurDrawBuffer : pMural->iCurReadBuffer] : 0;
1065 pData->cElements = 0;
1066
1067 pEl = &pData->aElements[pData->cElements];
1068 pEl->idFBO = pMural->fUseFBO ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0;
1069 pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
1070 pEl->posX = 0;
1071 pEl->posY = 0;
1072 pEl->width = width;
1073 pEl->height = height;
1074 pEl->enmFormat = GL_RGBA;
1075 pEl->enmType = GL_UNSIGNED_BYTE;
1076 pEl->cbData = width * height * 4;
1077 pEl->pvData = crCalloc(pEl->cbData);
1078 if (!pEl->pvData)
1079 {
1080 crVBoxServerFBImageDataTerm(pData);
1081 crWarning("crVBoxServerFBImageDataInit: crCalloc failed");
1082 return VERR_NO_MEMORY;
1083 }
1084 ++pData->cElements;
1085
1086 /* there is a lot of code that assumes we have double buffering, just assert here to print a warning in the log
1087 * so that we know that something irregular is going on */
1088 CRASSERT(pCtxInfo->CreateInfo.visualBits & CR_DOUBLE_BIT);
1089 if ((pCtxInfo->CreateInfo.visualBits & CR_DOUBLE_BIT)
1090 || version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL /* <- older version had a typo which lead to back always being used,
1091 * no matter what the visual bits are */
1092 )
1093 {
1094 pEl = &pData->aElements[pData->cElements];
1095 pEl->idFBO = pMural->fUseFBO ? pMural->aidFBOs[CR_SERVER_FBO_BB_IDX(pMural)] : 0;
1096 pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_BACK;
1097 pEl->posX = 0;
1098 pEl->posY = 0;
1099 pEl->width = width;
1100 pEl->height = height;
1101 pEl->enmFormat = GL_RGBA;
1102 pEl->enmType = GL_UNSIGNED_BYTE;
1103 pEl->cbData = width * height * 4;
1104 pEl->pvData = crCalloc(pEl->cbData);
1105 if (!pEl->pvData)
1106 {
1107 crVBoxServerFBImageDataTerm(pData);
1108 crWarning("crVBoxServerFBImageDataInit: crCalloc failed");
1109 return VERR_NO_MEMORY;
1110 }
1111 ++pData->cElements;
1112 }
1113
1114 if (version < SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER)
1115 return VINF_SUCCESS;
1116
1117
1118 if (version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL)
1119 {
1120/* if (pCtxInfo->CreateInfo.visualBits & CR_DEPTH_BIT) */ /* <- older version had a typo which lead to back always being used,
1121 * no matter what the visual bits are */
1122 {
1123 AssertCompile(sizeof (GLfloat) == 4);
1124 pEl = &pData->aElements[pData->cElements];
1125 pEl->idFBO = pMural->fUseFBO ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0;
1126 pEl->enmBuffer = 0; /* we do not care */
1127 pEl->posX = 0;
1128 pEl->posY = 0;
1129 pEl->width = width;
1130 pEl->height = height;
1131 pEl->enmFormat = GL_DEPTH_COMPONENT;
1132 pEl->enmType = GL_FLOAT;
1133 pEl->cbData = width * height * 4;
1134 pEl->pvData = crCalloc(pEl->cbData);
1135 if (!pEl->pvData)
1136 {
1137 crVBoxServerFBImageDataTerm(pData);
1138 crWarning("crVBoxServerFBImageDataInit: crCalloc failed");
1139 return VERR_NO_MEMORY;
1140 }
1141
1142 /* init to default depth value, just in case */
1143 pF = (GLfloat*)pEl->pvData;
1144 for (i = 0; i < width * height; ++i)
1145 {
1146 pF[i] = 1.;
1147 }
1148 ++pData->cElements;
1149 }
1150
1151 /* if (pCtxInfo->CreateInfo.visualBits & CR_STENCIL_BIT) */ /* <- older version had a typo which lead to back always being used,
1152 * no matter what the visual bits are */
1153 {
1154 AssertCompile(sizeof (GLuint) == 4);
1155 pEl = &pData->aElements[pData->cElements];
1156 pEl->idFBO = pMural->fUseFBO ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0;
1157 pEl->enmBuffer = 0; /* we do not care */
1158 pEl->posX = 0;
1159 pEl->posY = 0;
1160 pEl->width = width;
1161 pEl->height = height;
1162 pEl->enmFormat = GL_STENCIL_INDEX;
1163 pEl->enmType = GL_UNSIGNED_INT;
1164 pEl->cbData = width * height * 4;
1165 pEl->pvData = crCalloc(pEl->cbData);
1166 if (!pEl->pvData)
1167 {
1168 crVBoxServerFBImageDataTerm(pData);
1169 crWarning("crVBoxServerFBImageDataInit: crCalloc failed");
1170 return VERR_NO_MEMORY;
1171 }
1172 ++pData->cElements;
1173 }
1174 return VINF_SUCCESS;
1175 }
1176
1177 if ((pCtxInfo->CreateInfo.visualBits & CR_STENCIL_BIT)
1178 || (pCtxInfo->CreateInfo.visualBits & CR_DEPTH_BIT))
1179 {
1180 pEl = &pData->aElements[pData->cElements];
1181 pEl->idFBO = pMural->fUseFBO ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0;
1182 pEl->enmBuffer = 0; /* we do not care */
1183 pEl->posX = 0;
1184 pEl->posY = 0;
1185 pEl->width = width;
1186 pEl->height = height;
1187 pEl->enmFormat = GL_DEPTH_STENCIL;
1188 pEl->enmType = GL_UNSIGNED_INT_24_8;
1189 pEl->cbData = width * height * 4;
1190 pEl->pvData = crCalloc(pEl->cbData);
1191 if (!pEl->pvData)
1192 {
1193 crVBoxServerFBImageDataTerm(pData);
1194 crWarning("crVBoxServerFBImageDataInit: crCalloc failed");
1195 return VERR_NO_MEMORY;
1196 }
1197 ++pData->cElements;
1198 }
1199 return VINF_SUCCESS;
1200}
1201
1202static int crVBoxServerFBImageDataInit(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite)
1203{
1204 return crVBoxServerFBImageDataInitEx(pData, pCtxInfo, pMural, fWrite, SHCROGL_SSM_VERSION, 0, 0);
1205}
1206
1207static int crVBoxServerSaveFBImage(PSSMHANDLE pSSM)
1208{
1209 CRContextInfo *pCtxInfo;
1210 CRContext *pContext;
1211 CRMuralInfo *pMural;
1212 int32_t rc;
1213 GLuint i;
1214 struct
1215 {
1216 CRFBData data;
1217 CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */
1218 } Data;
1219
1220 Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4]));
1221
1222 pCtxInfo = cr_server.currentCtxInfo;
1223 pContext = pCtxInfo->pContext;
1224 pMural = pCtxInfo->currentMural;
1225
1226 rc = crVBoxServerFBImageDataInit(&Data.data, pCtxInfo, pMural, GL_FALSE);
1227 if (!RT_SUCCESS(rc))
1228 {
1229 crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
1230 return rc;
1231 }
1232
1233 rc = crStateAcquireFBImage(pContext, &Data.data);
1234 AssertRCReturn(rc, rc);
1235
1236 for (i = 0; i < Data.data.cElements; ++i)
1237 {
1238 CRFBDataElement * pEl = &Data.data.aElements[i];
1239 rc = SSMR3PutMem(pSSM, pEl->pvData, pEl->cbData);
1240 AssertRCReturn(rc, rc);
1241 }
1242
1243 crVBoxServerFBImageDataTerm(&Data.data);
1244
1245 return VINF_SUCCESS;
1246}
1247
1248#define CRSERVER_ASSERTRC_RETURN_VOID(_rc) do { \
1249 if(!RT_SUCCESS((_rc))) { \
1250 AssertFailed(); \
1251 return; \
1252 } \
1253 } while (0)
1254
1255static void crVBoxServerSaveAdditionalMuralsCB(unsigned long key, void *data1, void *data2)
1256{
1257 CRContextInfo *pContextInfo = (CRContextInfo *) data1;
1258 PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2;
1259 CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key);
1260 PSSMHANDLE pSSM = pData->pSSM;
1261 CRbitvalue initialCtxUsage[CR_MAX_BITARRAY];
1262 CRMuralInfo *pInitialCurMural = pContextInfo->currentMural;
1263
1264 crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage));
1265
1266 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1267
1268 pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key));
1269 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1270
1271 pData->rc = SSMR3PutMem(pSSM, &pContextInfo->CreateInfo.externalID, sizeof(pContextInfo->CreateInfo.externalID));
1272 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1273
1274 crServerPerformMakeCurrent(pMural, pContextInfo);
1275
1276 pData->rc = crVBoxServerSaveFBImage(pSSM);
1277
1278 /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */
1279 crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage));
1280 pContextInfo->currentMural = pInitialCurMural;
1281
1282 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1283}
1284
1285static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2)
1286{
1287 CRContextInfo *pContextInfo = (CRContextInfo *) data1;
1288 CRContext *pContext = pContextInfo->pContext;
1289 PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2;
1290 PSSMHANDLE pSSM = pData->pSSM;
1291 CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(pData->contextMuralTable, key);
1292 CRMuralInfo *pContextCurrentMural = pContextInfo->currentMural;
1293 const int32_t i32Dummy = 0;
1294
1295 AssertCompile(sizeof (i32Dummy) == sizeof (pMural->CreateInfo.externalID));
1296 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1297
1298 CRASSERT(pContext && pSSM);
1299 CRASSERT(pMural);
1300 CRASSERT(pMural->CreateInfo.externalID);
1301
1302 /* We could have skipped saving the key and use similar callback to load context states back,
1303 * but there's no guarantee we'd traverse hashtable in same order after loading.
1304 */
1305 pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key));
1306 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1307
1308#ifdef DEBUG_misha
1309 {
1310 unsigned long id;
1311 if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id))
1312 crWarning("No client id for server ctx %d", pContextInfo->CreateInfo.externalID);
1313 else
1314 CRASSERT(id == key);
1315 }
1316#endif
1317
1318#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
1319 if (pContextInfo->currentMural
1320 || crHashtableSearch(cr_server.muralTable, pMural->CreateInfo.externalID) /* <- this is not a dummy mural */
1321 )
1322 {
1323 CRASSERT(pMural->CreateInfo.externalID);
1324 CRASSERT(!crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID));
1325 pData->rc = SSMR3PutMem(pSSM, &pMural->CreateInfo.externalID, sizeof(pMural->CreateInfo.externalID));
1326 }
1327 else
1328 {
1329 /* this is a dummy mural */
1330 CRASSERT(!pMural->width);
1331 CRASSERT(!pMural->height);
1332 CRASSERT(crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID));
1333 pData->rc = SSMR3PutMem(pSSM, &i32Dummy, sizeof(pMural->CreateInfo.externalID));
1334 }
1335 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1336
1337 CRASSERT(CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pContext));
1338 CRASSERT(pContextInfo->currentMural == pMural || !pContextInfo->currentMural);
1339 CRASSERT(cr_server.curClient);
1340
1341 crServerPerformMakeCurrent(pMural, pContextInfo);
1342#endif
1343
1344 pData->rc = crStateSaveContext(pContext, pSSM);
1345 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1346
1347 pData->rc = crVBoxServerSaveFBImage(pSSM);
1348 CRSERVER_ASSERTRC_RETURN_VOID(pData->rc);
1349
1350 /* restore the initial current mural */
1351 pContextInfo->currentMural = pContextCurrentMural;
1352}
1353
1354#if 0
1355typedef struct CR_SERVER_CHECK_BUFFERS
1356{
1357 CRBufferObject *obj;
1358 CRContext *ctx;
1359}CR_SERVER_CHECK_BUFFERS, *PCR_SERVER_CHECK_BUFFERS;
1360
1361static void crVBoxServerCheckConsistencyContextBuffersCB(unsigned long key, void *data1, void *data2)
1362{
1363 CRContextInfo* pContextInfo = (CRContextInfo*)data1;
1364 CRContext *ctx = pContextInfo->pContext;
1365 PCR_SERVER_CHECK_BUFFERS pBuffers = (PCR_SERVER_CHECK_BUFFERS)data2;
1366 CRBufferObject *obj = pBuffers->obj;
1367 CRBufferObjectState *b = &(ctx->bufferobject);
1368 int j, k;
1369
1370 if (obj == b->arrayBuffer)
1371 {
1372 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1373 pBuffers->ctx = ctx;
1374 }
1375 if (obj == b->elementsBuffer)
1376 {
1377 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1378 pBuffers->ctx = ctx;
1379 }
1380#ifdef CR_ARB_pixel_buffer_object
1381 if (obj == b->packBuffer)
1382 {
1383 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1384 pBuffers->ctx = ctx;
1385 }
1386 if (obj == b->unpackBuffer)
1387 {
1388 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1389 pBuffers->ctx = ctx;
1390 }
1391#endif
1392
1393#ifdef CR_ARB_vertex_buffer_object
1394 for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j)
1395 {
1396 CRClientPointer *cp = crStateGetClientPointerByIndex(j, &ctx->client.array);
1397 if (obj == cp->buffer)
1398 {
1399 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1400 pBuffers->ctx = ctx;
1401 }
1402 }
1403
1404 for (k=0; k<ctx->client.vertexArrayStackDepth; ++k)
1405 {
1406 CRVertexArrays *pArray = &ctx->client.vertexArrayStack[k];
1407 for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j)
1408 {
1409 CRClientPointer *cp = crStateGetClientPointerByIndex(j, pArray);
1410 if (obj == cp->buffer)
1411 {
1412 Assert(!pBuffers->ctx || pBuffers->ctx == ctx);
1413 pBuffers->ctx = ctx;
1414 }
1415 }
1416 }
1417#endif
1418}
1419
1420static void crVBoxServerCheckConsistencyBuffersCB(unsigned long key, void *data1, void *data2)
1421{
1422 CRBufferObject *obj = (CRBufferObject *)data1;
1423 CR_SERVER_CHECK_BUFFERS Buffers = {0};
1424 Buffers.obj = obj;
1425 crHashtableWalk(cr_server.contextTable, crVBoxServerCheckConsistencyContextBuffersCB, (void*)&Buffers);
1426}
1427
1428//static void crVBoxServerCheckConsistency2CB(unsigned long key, void *data1, void *data2)
1429//{
1430// CRContextInfo* pContextInfo1 = (CRContextInfo*)data1;
1431// CRContextInfo* pContextInfo2 = (CRContextInfo*)data2;
1432//
1433// CRASSERT(pContextInfo1->pContext);
1434// CRASSERT(pContextInfo2->pContext);
1435//
1436// if (pContextInfo1 == pContextInfo2)
1437// {
1438// CRASSERT(pContextInfo1->pContext == pContextInfo2->pContext);
1439// return;
1440// }
1441//
1442// CRASSERT(pContextInfo1->pContext != pContextInfo2->pContext);
1443// CRASSERT(pContextInfo1->pContext->shared);
1444// CRASSERT(pContextInfo2->pContext->shared);
1445// CRASSERT(pContextInfo1->pContext->shared == pContextInfo2->pContext->shared);
1446// if (pContextInfo1->pContext->shared != pContextInfo2->pContext->shared)
1447// return;
1448//
1449// crHashtableWalk(pContextInfo1->pContext->shared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, pContextInfo2);
1450//}
1451static void crVBoxServerCheckSharedCB(unsigned long key, void *data1, void *data2)
1452{
1453 CRContextInfo* pContextInfo = (CRContextInfo*)data1;
1454 void **ppShared = (void**)data2;
1455 if (!*ppShared)
1456 *ppShared = pContextInfo->pContext->shared;
1457 else
1458 Assert(pContextInfo->pContext->shared == *ppShared);
1459}
1460
1461static void crVBoxServerCheckConsistency()
1462{
1463 CRSharedState *pShared = NULL;
1464 crHashtableWalk(cr_server.contextTable, crVBoxServerCheckSharedCB, (void*)&pShared);
1465 Assert(pShared);
1466 if (pShared)
1467 {
1468 crHashtableWalk(pShared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, NULL);
1469 }
1470}
1471#endif
1472
1473static uint32_t g_hackVBoxServerSaveLoadCallsLeft = 0;
1474
1475DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM)
1476{
1477 int32_t rc, i;
1478 uint32_t ui32;
1479 GLboolean b;
1480 unsigned long key;
1481 GLenum err;
1482#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
1483 CRClient *curClient;
1484 CRMuralInfo *curMural = NULL;
1485 CRContextInfo *curCtxInfo = NULL;
1486#endif
1487 CRVBOX_SAVE_STATE_GLOBAL Data;
1488
1489 crMemset(&Data, 0, sizeof (Data));
1490
1491#if 0
1492 crVBoxServerCheckConsistency();
1493#endif
1494
1495 /* We shouldn't be called if there's no clients at all*/
1496 CRASSERT(cr_server.numClients>0);
1497
1498 /* @todo it's hack atm */
1499 /* We want to be called only once to save server state but atm we're being called from svcSaveState
1500 * for every connected client (e.g. guest opengl application)
1501 */
1502 if (!cr_server.bIsInSavingState) /* It's first call */
1503 {
1504 cr_server.bIsInSavingState = GL_TRUE;
1505
1506 /* Store number of clients */
1507 rc = SSMR3PutU32(pSSM, (uint32_t) cr_server.numClients);
1508 AssertRCReturn(rc, rc);
1509
1510 g_hackVBoxServerSaveLoadCallsLeft = cr_server.numClients;
1511 }
1512
1513 g_hackVBoxServerSaveLoadCallsLeft--;
1514
1515 /* Do nothing until we're being called last time */
1516 if (g_hackVBoxServerSaveLoadCallsLeft>0)
1517 {
1518 return VINF_SUCCESS;
1519 }
1520
1521#ifdef DEBUG_misha
1522#define CR_DBG_STR_STATE_SAVE_START "VBox.Cr.StateSaveStart"
1523#define CR_DBG_STR_STATE_SAVE_STOP "VBox.Cr.StateSaveStop"
1524
1525 if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
1526 cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_START), CR_DBG_STR_STATE_SAVE_START);
1527#endif
1528
1529 /* Save rendering contexts creation info */
1530 ui32 = crHashtableNumElements(cr_server.contextTable);
1531 rc = SSMR3PutU32(pSSM, (uint32_t) ui32);
1532 AssertRCReturn(rc, rc);
1533 crHashtableWalk(cr_server.contextTable, crVBoxServerSaveCreateInfoFromCtxInfoCB, pSSM);
1534
1535#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
1536 curClient = cr_server.curClient;
1537 /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */
1538 if (curClient)
1539 {
1540 curCtxInfo = cr_server.curClient->currentCtxInfo;
1541 curMural = cr_server.curClient->currentMural;
1542 }
1543 else if (cr_server.numClients)
1544 {
1545 cr_server.curClient = cr_server.clients[0];
1546 }
1547#endif
1548
1549 /* first save windows info */
1550 /* Save windows creation info */
1551 ui32 = crHashtableNumElements(cr_server.muralTable);
1552 /* There should be default mural always */
1553 CRASSERT(ui32>=1);
1554 rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1);
1555 AssertRCReturn(rc, rc);
1556 crHashtableWalk(cr_server.muralTable, crVBoxServerSaveCreateInfoFromMuralInfoCB, pSSM);
1557
1558 /* Save cr_server.muralTable
1559 * @todo we don't need it all, just geometry info actually
1560 */
1561 rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1);
1562 AssertRCReturn(rc, rc);
1563 crHashtableWalk(cr_server.muralTable, crVBoxServerSaveMuralCB, pSSM);
1564
1565 /* we need to save front & backbuffer data for each mural first create a context -> mural association */
1566 crVBoxServerBuildSaveStateGlobal(&Data);
1567
1568 rc = crStateSaveGlobals(pSSM);
1569 AssertRCReturn(rc, rc);
1570
1571 Data.pSSM = pSSM;
1572 /* Save contexts state tracker data */
1573 /* @todo For now just some blind data dumps,
1574 * but I've a feeling those should be saved/restored in a very strict sequence to
1575 * allow diff_api to work correctly.
1576 * Should be tested more with multiply guest opengl apps working when saving VM snapshot.
1577 */
1578 crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, &Data);
1579 AssertRCReturn(Data.rc, Data.rc);
1580
1581 ui32 = crHashtableNumElements(Data.additionalMuralContextTable);
1582 rc = SSMR3PutU32(pSSM, (uint32_t) ui32);
1583 AssertRCReturn(rc, rc);
1584
1585 crHashtableWalk(Data.additionalMuralContextTable, crVBoxServerSaveAdditionalMuralsCB, &Data);
1586 AssertRCReturn(Data.rc, Data.rc);
1587
1588#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
1589 cr_server.curClient = curClient;
1590 /* Restore original win and ctx IDs*/
1591 if (curClient && curMural && curCtxInfo)
1592 {
1593 crServerPerformMakeCurrent(curMural, curCtxInfo);
1594 }
1595 else
1596 {
1597 cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE;
1598 }
1599#endif
1600
1601 /* Save clients info */
1602 for (i = 0; i < cr_server.numClients; i++)
1603 {
1604 if (cr_server.clients[i] && cr_server.clients[i]->conn)
1605 {
1606 CRClient *pClient = cr_server.clients[i];
1607
1608 rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID);
1609 AssertRCReturn(rc, rc);
1610
1611 rc = SSMR3PutU32(pSSM, pClient->conn->vMajor);
1612 AssertRCReturn(rc, rc);
1613
1614 rc = SSMR3PutU32(pSSM, pClient->conn->vMinor);
1615 AssertRCReturn(rc, rc);
1616
1617 rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient));
1618 AssertRCReturn(rc, rc);
1619
1620 if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber>=0)
1621 {
1622 b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtxInfo, &key);
1623 CRASSERT(b);
1624 rc = SSMR3PutMem(pSSM, &key, sizeof(key));
1625 AssertRCReturn(rc, rc);
1626 }
1627
1628 if (pClient->currentMural && pClient->currentWindow>=0)
1629 {
1630 b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key);
1631 CRASSERT(b);
1632 rc = SSMR3PutMem(pSSM, &key, sizeof(key));
1633 AssertRCReturn(rc, rc);
1634 }
1635 }
1636 }
1637
1638 /* all context gl error states should have now be synced with chromium erro states,
1639 * reset the error if any */
1640 while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR)
1641 crWarning("crServer: glGetError %d after saving snapshot", err);
1642
1643 cr_server.bIsInSavingState = GL_FALSE;
1644
1645#ifdef DEBUG_misha
1646 if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
1647 cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_STOP), CR_DBG_STR_STATE_SAVE_STOP);
1648#endif
1649
1650 return VINF_SUCCESS;
1651}
1652
1653static DECLCALLBACK(CRContext*) crVBoxServerGetContextCB(void* pvData)
1654{
1655 CRContextInfo* pContextInfo = (CRContextInfo*)pvData;
1656 CRASSERT(pContextInfo);
1657 CRASSERT(pContextInfo->pContext);
1658 return pContextInfo->pContext;
1659}
1660
1661static int32_t crVBoxServerLoadMurals(PSSMHANDLE pSSM, uint32_t version)
1662{
1663 unsigned long key;
1664 uint32_t ui, uiNumElems;
1665 /* Load windows */
1666 int32_t rc = SSMR3GetU32(pSSM, &uiNumElems);
1667 AssertRCReturn(rc, rc);
1668 for (ui=0; ui<uiNumElems; ++ui)
1669 {
1670 CRCreateInfo_t createInfo;
1671 char psz[200];
1672 GLint winID;
1673 unsigned long key;
1674
1675 rc = SSMR3GetMem(pSSM, &key, sizeof(key));
1676 AssertRCReturn(rc, rc);
1677 rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo));
1678 AssertRCReturn(rc, rc);
1679
1680 if (createInfo.pszDpyName)
1681 {
1682 rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL);
1683 AssertRCReturn(rc, rc);
1684 createInfo.pszDpyName = psz;
1685 }
1686
1687 winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key);
1688 CRASSERT((int64_t)winID == (int64_t)key);
1689 }
1690
1691 /* Load cr_server.muralTable */
1692 rc = SSMR3GetU32(pSSM, &uiNumElems);
1693 AssertRCReturn(rc, rc);
1694 for (ui=0; ui<uiNumElems; ++ui)
1695 {
1696 CRMuralInfo muralInfo;
1697 CRMuralInfo *pActualMural = NULL;
1698
1699 rc = SSMR3GetMem(pSSM, &key, sizeof(key));
1700 AssertRCReturn(rc, rc);
1701 rc = SSMR3GetMem(pSSM, &muralInfo, RT_OFFSETOF(CRMuralInfo, CreateInfo));
1702 AssertRCReturn(rc, rc);
1703
1704 if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING)
1705 muralInfo.bFbDraw = GL_TRUE;
1706
1707 if (muralInfo.pVisibleRects)
1708 {
1709 muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects);
1710 if (!muralInfo.pVisibleRects)
1711 {
1712 return VERR_NO_MEMORY;
1713 }
1714
1715 rc = SSMR3GetMem(pSSM, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects);
1716 AssertRCReturn(rc, rc);
1717 }
1718
1719 pActualMural = (CRMuralInfo *)crHashtableSearch(cr_server.muralTable, key);;
1720 CRASSERT(pActualMural);
1721
1722 if (version >= SHCROGL_SSM_VERSION_WITH_WINDOW_CTX_USAGE)
1723 {
1724 rc = SSMR3GetMem(pSSM, pActualMural->ctxUsage, sizeof (pActualMural->ctxUsage));
1725 CRASSERT(rc == VINF_SUCCESS);
1726 }
1727
1728 /* Restore windows geometry info */
1729 crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height);
1730 crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY);
1731 /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/
1732 if (muralInfo.bReceivedRects)
1733 {
1734 crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects);
1735 }
1736 crServerDispatchWindowShow(key, muralInfo.bVisible);
1737
1738 if (muralInfo.pVisibleRects)
1739 {
1740 crFree(muralInfo.pVisibleRects);
1741 }
1742
1743 Assert(!pActualMural->fDataPresented);
1744
1745 if (version >= SHCROGL_SSM_VERSION_WITH_PRESENT_STATE)
1746 pActualMural->fDataPresented = muralInfo.fDataPresented;
1747 else
1748 pActualMural->fDataPresented = crServerVBoxCompositionPresentNeeded(pActualMural);
1749 }
1750
1751 CRASSERT(RT_SUCCESS(rc));
1752 return VINF_SUCCESS;
1753}
1754
1755static int crVBoxServerLoadFBImage(PSSMHANDLE pSSM, uint32_t version,
1756 CRContextInfo* pContextInfo, CRMuralInfo *pMural)
1757{
1758 CRContext *pContext = pContextInfo->pContext;
1759 int32_t rc = VINF_SUCCESS;
1760 GLuint i;
1761 /* can apply the data right away */
1762 struct
1763 {
1764 CRFBData data;
1765 CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */
1766 } Data;
1767
1768 Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4]));
1769
1770 if (version >= SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER)
1771 {
1772 if (!pMural->width || !pMural->height)
1773 return VINF_SUCCESS;
1774
1775 rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, 0, 0);
1776 if (!RT_SUCCESS(rc))
1777 {
1778 crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
1779 return rc;
1780 }
1781 }
1782 else
1783 {
1784 GLint storedWidth, storedHeight;
1785
1786 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
1787 {
1788 CRASSERT(cr_server.currentCtxInfo == pContextInfo);
1789 CRASSERT(cr_server.currentMural = pMural);
1790 storedWidth = pMural->width;
1791 storedHeight = pMural->height;
1792 }
1793 else
1794 {
1795 storedWidth = pContext->buffer.storedWidth;
1796 storedHeight = pContext->buffer.storedHeight;
1797 }
1798
1799 if (!storedWidth || !storedHeight)
1800 return VINF_SUCCESS;
1801
1802 rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, storedWidth, storedHeight);
1803 if (!RT_SUCCESS(rc))
1804 {
1805 crWarning("crVBoxServerFBImageDataInit failed rc %d", rc);
1806 return rc;
1807 }
1808 }
1809
1810 CRASSERT(Data.data.cElements);
1811
1812 for (i = 0; i < Data.data.cElements; ++i)
1813 {
1814 CRFBDataElement * pEl = &Data.data.aElements[i];
1815 rc = SSMR3GetMem(pSSM, pEl->pvData, pEl->cbData);
1816 AssertRCReturn(rc, rc);
1817 }
1818
1819 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
1820 {
1821 CRBufferState *pBuf = &pContext->buffer;
1822 /* can apply the data right away */
1823 CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo);
1824 CRASSERT(cr_server.currentMural);
1825
1826 cr_server.head_spu->dispatch_table.MakeCurrent( pMural->spuWindow,
1827 0,
1828 pContextInfo->SpuContext >= 0
1829 ? pContextInfo->SpuContext
1830 : cr_server.MainContextInfo.SpuContext);
1831 crStateApplyFBImage(pContext, &Data.data);
1832 CRASSERT(!pBuf->pFrontImg);
1833 CRASSERT(!pBuf->pBackImg);
1834 crVBoxServerFBImageDataTerm(&Data.data);
1835
1836 if (pMural->fUseFBO && pMural->fDataPresented && crServerVBoxCompositionPresentNeeded(pMural))
1837 {
1838 crServerPresentFBO(pMural);
1839 }
1840
1841 CRASSERT(cr_server.currentMural);
1842 cr_server.head_spu->dispatch_table.MakeCurrent( cr_server.currentMural->spuWindow,
1843 0,
1844 cr_server.currentCtxInfo->SpuContext >= 0
1845 ? cr_server.currentCtxInfo->SpuContext
1846 : cr_server.MainContextInfo.SpuContext);
1847 }
1848 else
1849 {
1850 CRBufferState *pBuf = &pContext->buffer;
1851 CRASSERT(!pBuf->pFrontImg);
1852 CRASSERT(!pBuf->pBackImg);
1853 CRASSERT(Data.data.cElements); /* <- older versions always saved front and back, and we filtered out the null-sized buffers above */
1854
1855 if (Data.data.cElements)
1856 {
1857 CRFBData *pLazyData = crAlloc(RT_OFFSETOF(CRFBData, aElements[Data.data.cElements]));
1858 if (!RT_SUCCESS(rc))
1859 {
1860 crVBoxServerFBImageDataTerm(&Data.data);
1861 crWarning("crAlloc failed");
1862 return VERR_NO_MEMORY;
1863 }
1864
1865 crMemcpy(pLazyData, &Data.data, RT_OFFSETOF(CRFBData, aElements[Data.data.cElements]));
1866 pBuf->pFrontImg = pLazyData;
1867 }
1868 }
1869
1870 CRASSERT(RT_SUCCESS(rc));
1871 return VINF_SUCCESS;
1872}
1873
1874DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version)
1875{
1876 int32_t rc, i;
1877 uint32_t ui, uiNumElems;
1878 unsigned long key;
1879 GLenum err;
1880
1881 if (!cr_server.bIsInLoadingState)
1882 {
1883 /* AssertRCReturn(...) will leave us in loading state, but it doesn't matter as we'd be failing anyway */
1884 cr_server.bIsInLoadingState = GL_TRUE;
1885
1886 /* Read number of clients */
1887 rc = SSMR3GetU32(pSSM, &g_hackVBoxServerSaveLoadCallsLeft);
1888 AssertRCReturn(rc, rc);
1889 }
1890
1891 g_hackVBoxServerSaveLoadCallsLeft--;
1892
1893 /* Do nothing until we're being called last time */
1894 if (g_hackVBoxServerSaveLoadCallsLeft>0)
1895 {
1896 return VINF_SUCCESS;
1897 }
1898
1899 if (version < SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
1900 {
1901 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1902 }
1903
1904#ifdef DEBUG_misha
1905#define CR_DBG_STR_STATE_LOAD_START "VBox.Cr.StateLoadStart"
1906#define CR_DBG_STR_STATE_LOAD_STOP "VBox.Cr.StateLoadStop"
1907
1908 if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
1909 cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_START), CR_DBG_STR_STATE_LOAD_START);
1910#endif
1911
1912 /* Load and recreate rendering contexts */
1913 rc = SSMR3GetU32(pSSM, &uiNumElems);
1914 AssertRCReturn(rc, rc);
1915 for (ui=0; ui<uiNumElems; ++ui)
1916 {
1917 CRCreateInfo_t createInfo;
1918 char psz[200];
1919 GLint ctxID;
1920 CRContextInfo* pContextInfo;
1921 CRContext* pContext;
1922
1923 rc = SSMR3GetMem(pSSM, &key, sizeof(key));
1924 AssertRCReturn(rc, rc);
1925 rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo));
1926 AssertRCReturn(rc, rc);
1927
1928 if (createInfo.pszDpyName)
1929 {
1930 rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL);
1931 AssertRCReturn(rc, rc);
1932 createInfo.pszDpyName = psz;
1933 }
1934
1935 ctxID = crServerDispatchCreateContextEx(createInfo.pszDpyName, createInfo.visualBits, 0, key, createInfo.externalID /* <-saved state stores internal id here*/);
1936 CRASSERT((int64_t)ctxID == (int64_t)key);
1937
1938 pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key);
1939 CRASSERT(pContextInfo);
1940 CRASSERT(pContextInfo->pContext);
1941 pContext = pContextInfo->pContext;
1942 pContext->shared->id=-1;
1943 }
1944
1945 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
1946 {
1947 /* we have a mural data here */
1948 rc = crVBoxServerLoadMurals(pSSM, version);
1949 AssertRCReturn(rc, rc);
1950 }
1951
1952 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA && uiNumElems)
1953 {
1954 /* set the current client to allow doing crServerPerformMakeCurrent later */
1955 CRASSERT(cr_server.numClients);
1956 cr_server.curClient = cr_server.clients[0];
1957 }
1958
1959 rc = crStateLoadGlobals(pSSM, version);
1960 AssertRCReturn(rc, rc);
1961
1962 if (uiNumElems)
1963 {
1964 /* ensure we have main context set up as current */
1965 CRMuralInfo *pMural;
1966 CRASSERT(cr_server.MainContextInfo.SpuContext > 0);
1967 CRASSERT(!cr_server.currentCtxInfo);
1968 CRASSERT(!cr_server.currentMural);
1969 pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.visualBits);
1970 CRASSERT(pMural);
1971 crServerPerformMakeCurrent(pMural, &cr_server.MainContextInfo);
1972 }
1973
1974 /* Restore context state data */
1975 for (ui=0; ui<uiNumElems; ++ui)
1976 {
1977 CRContextInfo* pContextInfo;
1978 CRContext *pContext;
1979 CRMuralInfo *pMural = NULL;
1980 int32_t winId = 0;
1981
1982 rc = SSMR3GetMem(pSSM, &key, sizeof(key));
1983 AssertRCReturn(rc, rc);
1984
1985 pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key);
1986 CRASSERT(pContextInfo);
1987 CRASSERT(pContextInfo->pContext);
1988 pContext = pContextInfo->pContext;
1989
1990 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
1991 {
1992 rc = SSMR3GetMem(pSSM, &winId, sizeof(winId));
1993 AssertRCReturn(rc, rc);
1994
1995 if (winId)
1996 {
1997 pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, winId);
1998 CRASSERT(pMural);
1999 }
2000 else
2001 {
2002 /* null winId means a dummy mural, get it */
2003 pMural = crServerGetDummyMural(pContextInfo->CreateInfo.visualBits);
2004 CRASSERT(pMural);
2005 }
2006 }
2007
2008 rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version);
2009 AssertRCReturn(rc, rc);
2010
2011 /*Restore front/back buffer images*/
2012 rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural);
2013 AssertRCReturn(rc, rc);
2014 }
2015
2016 if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA)
2017 {
2018 CRContextInfo *pContextInfo;
2019 CRMuralInfo *pMural;
2020 GLint ctxId;
2021
2022 rc = SSMR3GetU32(pSSM, &uiNumElems);
2023 AssertRCReturn(rc, rc);
2024 for (ui=0; ui<uiNumElems; ++ui)
2025 {
2026 CRbitvalue initialCtxUsage[CR_MAX_BITARRAY];
2027 CRMuralInfo *pInitialCurMural;
2028
2029 rc = SSMR3GetMem(pSSM, &key, sizeof(key));
2030 AssertRCReturn(rc, rc);
2031
2032 rc = SSMR3GetMem(pSSM, &ctxId, sizeof(ctxId));
2033 AssertRCReturn(rc, rc);
2034
2035 pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key);
2036 CRASSERT(pMural);
2037 if (ctxId)
2038 {
2039 pContextInfo = (CRContextInfo *)crHashtableSearch(cr_server.contextTable, ctxId);
2040 CRASSERT(pContextInfo);
2041 }
2042 else
2043 pContextInfo = &cr_server.MainContextInfo;
2044
2045 crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage));
2046 pInitialCurMural = pContextInfo->currentMural;
2047
2048 rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural);
2049 AssertRCReturn(rc, rc);
2050
2051 /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */
2052 crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage));
2053 pContextInfo->currentMural = pInitialCurMural;
2054 }
2055
2056 CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo);
2057
2058 cr_server.curClient = NULL;
2059 cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE;
2060 }
2061 else
2062 {
2063 CRServerFreeIDsPool_t dummyIdsPool;
2064
2065 /* we have a mural data here */
2066 rc = crVBoxServerLoadMurals(pSSM, version);
2067 AssertRCReturn(rc, rc);
2068
2069 /* not used any more, just read it out and ignore */
2070 rc = SSMR3GetMem(pSSM, &dummyIdsPool, sizeof(dummyIdsPool));
2071 CRASSERT(rc == VINF_SUCCESS);
2072 }
2073
2074 /* Load clients info */
2075 for (i = 0; i < cr_server.numClients; i++)
2076 {
2077 if (cr_server.clients[i] && cr_server.clients[i]->conn)
2078 {
2079 CRClient *pClient = cr_server.clients[i];
2080 CRClient client;
2081 unsigned long ctxID=-1, winID=-1;
2082
2083 rc = SSMR3GetU32(pSSM, &ui);
2084 AssertRCReturn(rc, rc);
2085 /* If this assert fires, then we should search correct client in the list first*/
2086 CRASSERT(ui == pClient->conn->u32ClientID);
2087
2088 if (version>=4)
2089 {
2090 rc = SSMR3GetU32(pSSM, &pClient->conn->vMajor);
2091 AssertRCReturn(rc, rc);
2092
2093 rc = SSMR3GetU32(pSSM, &pClient->conn->vMinor);
2094 AssertRCReturn(rc, rc);
2095 }
2096
2097 rc = SSMR3GetMem(pSSM, &client, sizeof(client));
2098 CRASSERT(rc == VINF_SUCCESS);
2099
2100 client.conn = pClient->conn;
2101 /* We can't reassign client number, as we'd get wrong results in TranslateTextureID
2102 * and fail to bind old textures.
2103 */
2104 /*client.number = pClient->number;*/
2105 *pClient = client;
2106
2107 pClient->currentContextNumber = -1;
2108 pClient->currentCtxInfo = &cr_server.MainContextInfo;
2109 pClient->currentMural = NULL;
2110 pClient->currentWindow = -1;
2111
2112 cr_server.curClient = pClient;
2113
2114 if (client.currentCtxInfo && client.currentContextNumber>=0)
2115 {
2116 rc = SSMR3GetMem(pSSM, &ctxID, sizeof(ctxID));
2117 AssertRCReturn(rc, rc);
2118 client.currentCtxInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, ctxID);
2119 CRASSERT(client.currentCtxInfo);
2120 CRASSERT(client.currentCtxInfo->pContext);
2121 //pClient->currentCtx = client.currentCtx;
2122 //pClient->currentContextNumber = ctxID;
2123 }
2124
2125 if (client.currentMural && client.currentWindow>=0)
2126 {
2127 rc = SSMR3GetMem(pSSM, &winID, sizeof(winID));
2128 AssertRCReturn(rc, rc);
2129 client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID);
2130 CRASSERT(client.currentMural);
2131 //pClient->currentMural = client.currentMural;
2132 //pClient->currentWindow = winID;
2133 }
2134
2135 /* Restore client active context and window */
2136 crServerDispatchMakeCurrent(winID, 0, ctxID);
2137
2138 if (0)
2139 {
2140// CRContext *tmpCtx;
2141// CRCreateInfo_t *createInfo;
2142 GLfloat one[4] = { 1, 1, 1, 1 };
2143 GLfloat amb[4] = { 0.4f, 0.4f, 0.4f, 1.0f };
2144
2145 crServerDispatchMakeCurrent(winID, 0, ctxID);
2146
2147 crHashtableWalk(client.currentCtxInfo->pContext->shared->textureTable, crVBoxServerSyncTextureCB, client.currentCtxInfo->pContext);
2148
2149 crStateTextureObjectDiff(client.currentCtxInfo->pContext, NULL, NULL, &client.currentCtxInfo->pContext->texture.base1D, GL_TRUE);
2150 crStateTextureObjectDiff(client.currentCtxInfo->pContext, NULL, NULL, &client.currentCtxInfo->pContext->texture.base2D, GL_TRUE);
2151 crStateTextureObjectDiff(client.currentCtxInfo->pContext, NULL, NULL, &client.currentCtxInfo->pContext->texture.base3D, GL_TRUE);
2152#ifdef CR_ARB_texture_cube_map
2153 crStateTextureObjectDiff(client.currentCtxInfo->pContext, NULL, NULL, &client.currentCtxInfo->pContext->texture.baseCubeMap, GL_TRUE);
2154#endif
2155#ifdef CR_NV_texture_rectangle
2156 //@todo this doesn't work as expected
2157 //crStateTextureObjectDiff(client.currentCtxInfo->pContext, NULL, NULL, &client.currentCtxInfo->pContext->texture.baseRect, GL_TRUE);
2158#endif
2159 /*cr_server.head_spu->dispatch_table.Materialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb);
2160 cr_server.head_spu->dispatch_table.LightModelfv(GL_LIGHT_MODEL_AMBIENT, amb);
2161 cr_server.head_spu->dispatch_table.Lightfv(GL_LIGHT1, GL_DIFFUSE, one);
2162
2163 cr_server.head_spu->dispatch_table.Enable(GL_LIGHTING);
2164 cr_server.head_spu->dispatch_table.Enable(GL_LIGHT0);
2165 cr_server.head_spu->dispatch_table.Enable(GL_LIGHT1);
2166
2167 cr_server.head_spu->dispatch_table.Enable(GL_CULL_FACE);
2168 cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_2D);*/
2169
2170 //crStateViewport( 0, 0, 600, 600 );
2171 //pClient->currentMural->viewportValidated = GL_FALSE;
2172 //cr_server.head_spu->dispatch_table.Viewport( 0, 0, 600, 600 );
2173
2174 //crStateMatrixMode(GL_PROJECTION);
2175 //cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION);
2176
2177 //crStateLoadIdentity();
2178 //cr_server.head_spu->dispatch_table.LoadIdentity();
2179
2180 //crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
2181 //cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
2182
2183 //crStateMatrixMode(GL_MODELVIEW);
2184 //cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW);
2185 //crServerDispatchLoadIdentity();
2186 //crStateFrustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
2187 //cr_server.head_spu->dispatch_table.Frustum(-0.5, 0.5, -0.5, 0.5, 1.5, 150.0);
2188 //crServerDispatchLoadIdentity();
2189
2190 /*createInfo = (CRCreateInfo_t *) crHashtableSearch(cr_server.pContextCreateInfoTable, ctxID);
2191 CRASSERT(createInfo);
2192 tmpCtx = crStateCreateContext(NULL, createInfo->visualBits, NULL);
2193 CRASSERT(tmpCtx);
2194 crStateDiffContext(tmpCtx, client.currentCtxInfo->pContext);
2195 crStateDestroyContext(tmpCtx);*/
2196 }
2197 }
2198 }
2199
2200 //crServerDispatchMakeCurrent(-1, 0, -1);
2201
2202 cr_server.curClient = NULL;
2203
2204 while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR)
2205 crWarning("crServer: glGetError %d after loading snapshot", err);
2206
2207 cr_server.bIsInLoadingState = GL_FALSE;
2208
2209#if 0
2210 crVBoxServerCheckConsistency();
2211#endif
2212
2213#ifdef DEBUG_misha
2214 if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY)
2215 cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_STOP), CR_DBG_STR_STATE_LOAD_STOP);
2216#endif
2217
2218 return VINF_SUCCESS;
2219}
2220
2221#define SCREEN(i) (cr_server.screen[i])
2222#define MAPPED(screen) ((screen).winID != 0)
2223
2224static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2)
2225{
2226 CRMuralInfo *pMI = (CRMuralInfo*) data1;
2227 int *sIndex = (int*) data2;
2228
2229 Assert(pMI->cDisabled);
2230
2231 if (pMI->screenId == *sIndex)
2232 {
2233 crServerVBoxCompositionDisableEnter(pMI);
2234
2235 renderspuReparentWindow(pMI->spuWindow);
2236
2237 crServerVBoxCompositionDisableLeave(pMI, GL_FALSE);
2238 }
2239}
2240
2241static void crVBoxServerCheckMuralCB(unsigned long key, void *data1, void *data2)
2242{
2243 CRMuralInfo *pMI = (CRMuralInfo*) data1;
2244 (void) data2;
2245
2246 crServerCheckMuralGeometry(pMI);
2247}
2248
2249DECLEXPORT(int32_t) crVBoxServerSetScreenCount(int sCount)
2250{
2251 int i;
2252
2253 if (sCount>CR_MAX_GUEST_MONITORS)
2254 return VERR_INVALID_PARAMETER;
2255
2256 /*Shouldn't happen yet, but to be safe in future*/
2257 for (i=0; i<cr_server.screenCount; ++i)
2258 {
2259 if (MAPPED(SCREEN(i)))
2260 crWarning("Screen count is changing, but screen[%i] is still mapped", i);
2261 return VERR_NOT_IMPLEMENTED;
2262 }
2263
2264 cr_server.screenCount = sCount;
2265
2266 for (i=0; i<sCount; ++i)
2267 {
2268 SCREEN(i).winID = 0;
2269 }
2270
2271 return VINF_SUCCESS;
2272}
2273
2274DECLEXPORT(int32_t) crVBoxServerUnmapScreen(int sIndex)
2275{
2276 crDebug("crVBoxServerUnmapScreen(%i)", sIndex);
2277
2278 if (sIndex<0 || sIndex>=cr_server.screenCount)
2279 return VERR_INVALID_PARAMETER;
2280
2281 if (MAPPED(SCREEN(sIndex)))
2282 {
2283 SCREEN(sIndex).winID = 0;
2284 renderspuSetWindowId(0);
2285
2286 crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex);
2287
2288 crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex);
2289 }
2290
2291 renderspuSetWindowId(SCREEN(0).winID);
2292 return VINF_SUCCESS;
2293}
2294
2295DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h, uint64_t winID)
2296{
2297 crDebug("crVBoxServerMapScreen(%i) [%i,%i:%u,%u %x]", sIndex, x, y, w, h, winID);
2298
2299 if (sIndex<0 || sIndex>=cr_server.screenCount)
2300 return VERR_INVALID_PARAMETER;
2301
2302 if (MAPPED(SCREEN(sIndex)) && SCREEN(sIndex).winID!=winID)
2303 {
2304 crDebug("Mapped screen[%i] is being remapped.", sIndex);
2305 crVBoxServerUnmapScreen(sIndex);
2306 }
2307
2308 SCREEN(sIndex).winID = winID;
2309 SCREEN(sIndex).x = x;
2310 SCREEN(sIndex).y = y;
2311 SCREEN(sIndex).w = w;
2312 SCREEN(sIndex).h = h;
2313
2314 renderspuSetWindowId(SCREEN(sIndex).winID);
2315 crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex);
2316
2317 crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex);
2318 renderspuSetWindowId(SCREEN(0).winID);
2319
2320 crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL);
2321
2322#ifndef WINDOWS
2323 /*Restore FB content for clients, which have current window on a screen being remapped*/
2324 {
2325 GLint i;
2326
2327 for (i = 0; i < cr_server.numClients; i++)
2328 {
2329 cr_server.curClient = cr_server.clients[i];
2330 if (cr_server.curClient->currentCtxInfo
2331 && cr_server.curClient->currentCtxInfo->pContext
2332 && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg)
2333 && cr_server.curClient->currentMural
2334 && cr_server.curClient->currentMural->screenId == sIndex
2335 && cr_server.curClient->currentCtxInfo->pContext->buffer.storedHeight == h
2336 && cr_server.curClient->currentCtxInfo->pContext->buffer.storedWidth == w)
2337 {
2338 int clientWindow = cr_server.curClient->currentWindow;
2339 int clientContext = cr_server.curClient->currentContextNumber;
2340 CRFBData *pLazyData = (CRFBData *)cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg;
2341
2342 if (clientWindow && clientWindow != cr_server.currentWindow)
2343 {
2344 crServerDispatchMakeCurrent(clientWindow, 0, clientContext);
2345 }
2346
2347 crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext, pLazyData);
2348 crStateFreeFBImageLegacy(cr_server.curClient->currentCtxInfo->pContext);
2349 }
2350 }
2351 cr_server.curClient = NULL;
2352 }
2353#endif
2354
2355 {
2356 PCR_DISPLAY pDisplay = crServerDisplayGetInitialized(sIndex);
2357 if (pDisplay)
2358 CrDpResize(pDisplay, w, h, w, h);
2359 }
2360
2361 return VINF_SUCCESS;
2362}
2363
2364static int crVBoxServerUpdateMuralRootVisibleRegion(CRMuralInfo *pMI)
2365{
2366 GLboolean fForcePresent;
2367 uint32_t cRects;
2368 const RTRECT *pRects;
2369 int rc;
2370
2371 fForcePresent = crServerVBoxCompositionPresentNeeded(pMI);
2372
2373 crServerVBoxCompositionDisableEnter(pMI);
2374
2375 if (cr_server.fRootVrOn)
2376 {
2377 if (!pMI->fRootVrOn)
2378 {
2379 VBOXVR_TEXTURE Tex = {0};
2380
2381 rc = CrVrScrCompositorInit(&pMI->RootVrCompositor);
2382 if (!RT_SUCCESS(rc))
2383 {
2384 crWarning("CrVrScrCompositorInit failed, rc %d", rc);
2385 return rc;
2386 }
2387
2388
2389 Tex.width = pMI->width;
2390 Tex.height = pMI->height;
2391 Tex.target = GL_TEXTURE_2D;
2392 Tex.hwid = 0;
2393 CrVrScrCompositorEntryInit(&pMI->RootVrCEntry, &Tex);
2394 }
2395
2396 rc = crServerMuralSynchRootVr(pMI, &cRects, &pRects);
2397 if (!RT_SUCCESS(rc))
2398 {
2399 crWarning("crServerMuralSynchRootVr failed, rc %d", rc);
2400 return rc;
2401 }
2402
2403 if (!pMI->fRootVrOn)
2404 CrVrScrCompositorEntryTexUpdate(&pMI->RootVrCEntry, CrVrScrCompositorEntryTexGet(&pMI->CEntry));
2405 }
2406 else
2407 {
2408 CrVrScrCompositorTerm(&pMI->RootVrCompositor);
2409 rc = CrVrScrCompositorEntryRegionsGet(&pMI->Compositor, &pMI->CEntry, &cRects, NULL, &pRects);
2410 if (!RT_SUCCESS(rc))
2411 {
2412 crWarning("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc);
2413 return rc;
2414 }
2415
2416 /* CEntry should always be in sync */
2417// CrVrScrCompositorEntryTexUpdate(&pMI->CEntry, CrVrScrCompositorEntryTexGet(&pMI->RootVrCEntry));
2418 }
2419
2420 cr_server.head_spu->dispatch_table.WindowVisibleRegion(pMI->spuWindow, cRects, pRects);
2421
2422 pMI->fRootVrOn = cr_server.fRootVrOn;
2423
2424 crServerVBoxCompositionDisableLeave(pMI, fForcePresent);
2425
2426 return rc;
2427}
2428
2429static void crVBoxServerSetRootVisibleRegionCB(unsigned long key, void *data1, void *data2)
2430{
2431 CRMuralInfo *pMI = (CRMuralInfo*) data1;
2432
2433 if (!pMI->CreateInfo.externalID)
2434 return;
2435 (void) data2;
2436
2437 crVBoxServerUpdateMuralRootVisibleRegion(pMI);
2438}
2439
2440DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, const RTRECT *pRects)
2441{
2442 int32_t rc = VINF_SUCCESS;
2443
2444 /* non-zero rects pointer indicate rects are present and switched on
2445 * i.e. cRects==0 and pRects!=NULL means root visible regioning is ON and there are no visible regions,
2446 * while pRects==NULL means root visible regioning is OFF, i.e. everything is visible */
2447 if (pRects)
2448 {
2449 crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint));
2450 rc = VBoxVrListRectsSet(&cr_server.RootVr, cRects, pRects, NULL);
2451 if (!RT_SUCCESS(rc))
2452 {
2453 crWarning("VBoxVrListRectsSet failed! rc %d", rc);
2454 return rc;
2455 }
2456
2457 cr_server.fRootVrOn = GL_TRUE;
2458 }
2459 else
2460 {
2461 if (!cr_server.fRootVrOn)
2462 return VINF_SUCCESS;
2463
2464 VBoxVrListClear(&cr_server.RootVr);
2465
2466 cr_server.fRootVrOn = GL_FALSE;
2467 }
2468
2469 crHashtableWalk(cr_server.muralTable, crVBoxServerSetRootVisibleRegionCB, NULL);
2470
2471 return VINF_SUCCESS;
2472}
2473
2474DECLEXPORT(void) crVBoxServerSetPresentFBOCB(PFNCRSERVERPRESENTFBO pfnPresentFBO)
2475{
2476 cr_server.pfnPresentFBO = pfnPresentFBO;
2477}
2478
2479int32_t crServerSetOffscreenRenderingMode(GLubyte value)
2480{
2481 if (cr_server.bForceOffscreenRendering==value)
2482 {
2483 return VINF_SUCCESS;
2484 }
2485
2486 if (value > CR_SERVER_REDIR_MAXVAL)
2487 {
2488 crWarning("crServerSetOffscreenRenderingMode: invalid arg: %d", value);
2489 return VERR_INVALID_PARAMETER;
2490 }
2491
2492 if (value && !crServerSupportRedirMuralFBO())
2493 {
2494 return VERR_NOT_SUPPORTED;
2495 }
2496
2497 cr_server.bForceOffscreenRendering=value;
2498
2499 crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL);
2500
2501 return VINF_SUCCESS;
2502}
2503
2504DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value)
2505{
2506 return crServerSetOffscreenRenderingMode(value ? CR_SERVER_REDIR_FBO_RAM : cr_server.bOffscreenRenderingDefault);
2507}
2508
2509DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks)
2510{
2511 /* No need for a synchronization as this is single threaded. */
2512 if (pCallbacks)
2513 {
2514 cr_server.outputRedirect = *pCallbacks;
2515 cr_server.bUseOutputRedirect = true;
2516 }
2517 else
2518 {
2519 cr_server.bUseOutputRedirect = false;
2520 }
2521
2522 // @todo dynamically intercept already existing output:
2523 // crHashtableWalk(cr_server.muralTable, crVBoxServerOutputRedirectCB, NULL);
2524
2525 return VINF_SUCCESS;
2526}
2527
2528static void crVBoxServerUpdateScreenViewportCB(unsigned long key, void *data1, void *data2)
2529{
2530 CRMuralInfo *mural = (CRMuralInfo*) data1;
2531 int *sIndex = (int*) data2;
2532
2533 if (mural->screenId != *sIndex)
2534 return;
2535
2536 crServerCheckMuralGeometry(mural);
2537}
2538
2539
2540DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h)
2541{
2542 CRScreenViewportInfo *pVieport;
2543 GLboolean fPosChanged, fSizeChanged;
2544
2545 crDebug("crVBoxServerSetScreenViewport(%i)", sIndex);
2546
2547 if (sIndex<0 || sIndex>=cr_server.screenCount)
2548 {
2549 crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex);
2550 return VERR_INVALID_PARAMETER;
2551 }
2552
2553 pVieport = &cr_server.screenVieport[sIndex];
2554 fPosChanged = (pVieport->x != x || pVieport->y != y);
2555 fSizeChanged = (pVieport->w != w || pVieport->h != h);
2556
2557 if (!fPosChanged && !fSizeChanged)
2558 {
2559 crDebug("crVBoxServerSetScreenViewport: no changes");
2560 return VINF_SUCCESS;
2561 }
2562
2563 if (fPosChanged)
2564 {
2565 pVieport->x = x;
2566 pVieport->y = y;
2567
2568 crHashtableWalk(cr_server.muralTable, crVBoxServerUpdateScreenViewportCB, &sIndex);
2569 }
2570
2571 if (fSizeChanged)
2572 {
2573 pVieport->w = w;
2574 pVieport->h = h;
2575
2576 /* no need to do anything here actually */
2577 }
2578 return VINF_SUCCESS;
2579}
2580
2581
2582#ifdef VBOX_WITH_CRHGSMI
2583/* We moved all CrHgsmi command processing to crserverlib to keep the logic of dealing with CrHgsmi commands in one place.
2584 *
2585 * 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.
2586 * This help avoiding the "blocked-client" issues. The client is blocked if another client is doing begin-end stuff.
2587 * For now we eliminated polling that could occur on block, which caused a higher-priority thread (in guest) polling for the blocked command complition
2588 * to block the lower-priority thread trying to complete the blocking command.
2589 * And removed extra memcpy done on blocked command arrival.
2590 *
2591 * In the future we will extend CrHgsmi functionality to maintain texture data directly in CrHgsmi allocation to avoid extra memcpy-ing with PBO,
2592 * implement command completion and stuff necessary for GPU scheduling to work properly for WDDM Windows guests, etc.
2593 *
2594 * NOTE: it is ALWAYS responsibility of the crVBoxServerCrHgsmiCmd to complete the command!
2595 * */
2596int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t cbCmd)
2597{
2598 int32_t rc;
2599 uint32_t cBuffers = pCmd->cBuffers;
2600 uint32_t cParams;
2601 uint32_t cbHdr;
2602 CRVBOXHGSMIHDR *pHdr;
2603 uint32_t u32Function;
2604 uint32_t u32ClientID;
2605 CRClient *pClient;
2606
2607 if (!g_pvVRamBase)
2608 {
2609 crWarning("g_pvVRamBase is not initialized");
2610 crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_STATE);
2611 return VINF_SUCCESS;
2612 }
2613
2614 if (!cBuffers)
2615 {
2616 crWarning("zero buffers passed in!");
2617 crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
2618 return VINF_SUCCESS;
2619 }
2620
2621 cParams = cBuffers-1;
2622
2623 cbHdr = pCmd->aBuffers[0].cbBuffer;
2624 pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR);
2625 if (!pHdr)
2626 {
2627 crWarning("invalid header buffer!");
2628 crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
2629 return VINF_SUCCESS;
2630 }
2631
2632 if (cbHdr < sizeof (*pHdr))
2633 {
2634 crWarning("invalid header buffer size!");
2635 crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER);
2636 return VINF_SUCCESS;
2637 }
2638
2639 u32Function = pHdr->u32Function;
2640 u32ClientID = pHdr->u32ClientID;
2641
2642 switch (u32Function)
2643 {
2644 case SHCRGL_GUEST_FN_WRITE:
2645 {
2646 crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE\n"));
2647
2648 /* @todo: Verify */
2649 if (cParams == 1)
2650 {
2651 CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr;
2652 VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
2653 /* Fetch parameters. */
2654 uint32_t cbBuffer = pBuf->cbBuffer;
2655 uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
2656
2657 if (cbHdr < sizeof (*pFnCmd))
2658 {
2659 crWarning("invalid write cmd buffer size!");
2660 rc = VERR_INVALID_PARAMETER;
2661 break;
2662 }
2663
2664 CRASSERT(cbBuffer);
2665 if (!pBuffer)
2666 {
2667 crWarning("invalid buffer data received from guest!");
2668 rc = VERR_INVALID_PARAMETER;
2669 break;
2670 }
2671
2672 rc = crVBoxServerClientGet(u32ClientID, &pClient);
2673 if (RT_FAILURE(rc))
2674 {
2675 break;
2676 }
2677
2678 /* This should never fire unless we start to multithread */
2679 CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
2680 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2681
2682 pClient->conn->pBuffer = pBuffer;
2683 pClient->conn->cbBuffer = cbBuffer;
2684 CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr);
2685 rc = crVBoxServerInternalClientWriteRead(pClient);
2686 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2687 return rc;
2688 }
2689 else
2690 {
2691 crWarning("invalid number of args");
2692 rc = VERR_INVALID_PARAMETER;
2693 break;
2694 }
2695 break;
2696 }
2697
2698 case SHCRGL_GUEST_FN_INJECT:
2699 {
2700 crDebug(("svcCall: SHCRGL_GUEST_FN_INJECT\n"));
2701
2702 /* @todo: Verify */
2703 if (cParams == 1)
2704 {
2705 CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr;
2706 /* Fetch parameters. */
2707 uint32_t u32InjectClientID = pFnCmd->u32ClientID;
2708 VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
2709 uint32_t cbBuffer = pBuf->cbBuffer;
2710 uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
2711
2712 if (cbHdr < sizeof (*pFnCmd))
2713 {
2714 crWarning("invalid inject cmd buffer size!");
2715 rc = VERR_INVALID_PARAMETER;
2716 break;
2717 }
2718
2719 CRASSERT(cbBuffer);
2720 if (!pBuffer)
2721 {
2722 crWarning("invalid buffer data received from guest!");
2723 rc = VERR_INVALID_PARAMETER;
2724 break;
2725 }
2726
2727 rc = crVBoxServerClientGet(u32InjectClientID, &pClient);
2728 if (RT_FAILURE(rc))
2729 {
2730 break;
2731 }
2732
2733 /* This should never fire unless we start to multithread */
2734 CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
2735 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2736
2737 pClient->conn->pBuffer = pBuffer;
2738 pClient->conn->cbBuffer = cbBuffer;
2739 CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr);
2740 rc = crVBoxServerInternalClientWriteRead(pClient);
2741 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2742 return rc;
2743 }
2744
2745 crWarning("invalid number of args");
2746 rc = VERR_INVALID_PARAMETER;
2747 break;
2748 }
2749
2750 case SHCRGL_GUEST_FN_READ:
2751 {
2752 crDebug(("svcCall: SHCRGL_GUEST_FN_READ\n"));
2753
2754 /* @todo: Verify */
2755 if (cParams == 1)
2756 {
2757 CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr;
2758 VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
2759 /* Fetch parameters. */
2760 uint32_t cbBuffer = pBuf->cbBuffer;
2761 uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
2762
2763 if (cbHdr < sizeof (*pFnCmd))
2764 {
2765 crWarning("invalid read cmd buffer size!");
2766 rc = VERR_INVALID_PARAMETER;
2767 break;
2768 }
2769
2770
2771 if (!pBuffer)
2772 {
2773 crWarning("invalid buffer data received from guest!");
2774 rc = VERR_INVALID_PARAMETER;
2775 break;
2776 }
2777
2778 rc = crVBoxServerClientGet(u32ClientID, &pClient);
2779 if (RT_FAILURE(rc))
2780 {
2781 break;
2782 }
2783
2784 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2785
2786 rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer);
2787
2788 /* Return the required buffer size always */
2789 pFnCmd->cbBuffer = cbBuffer;
2790
2791 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2792
2793 /* the read command is never pended, complete it right away */
2794 pHdr->result = rc;
2795 crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
2796 return VINF_SUCCESS;
2797 }
2798
2799 crWarning("invalid number of args");
2800 rc = VERR_INVALID_PARAMETER;
2801 break;
2802 }
2803
2804 case SHCRGL_GUEST_FN_WRITE_READ:
2805 {
2806 crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n"));
2807
2808 /* @todo: Verify */
2809 if (cParams == 2)
2810 {
2811 CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr;
2812 VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1];
2813 VBOXVDMACMD_CHROMIUM_BUFFER *pWbBuf = &pCmd->aBuffers[2];
2814
2815 /* Fetch parameters. */
2816 uint32_t cbBuffer = pBuf->cbBuffer;
2817 uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t);
2818
2819 uint32_t cbWriteback = pWbBuf->cbBuffer;
2820 char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char);
2821
2822 if (cbHdr < sizeof (*pFnCmd))
2823 {
2824 crWarning("invalid write_read cmd buffer size!");
2825 rc = VERR_INVALID_PARAMETER;
2826 break;
2827 }
2828
2829
2830 CRASSERT(cbBuffer);
2831 if (!pBuffer)
2832 {
2833 crWarning("invalid write buffer data received from guest!");
2834 rc = VERR_INVALID_PARAMETER;
2835 break;
2836 }
2837
2838 CRASSERT(cbWriteback);
2839 if (!pWriteback)
2840 {
2841 crWarning("invalid writeback buffer data received from guest!");
2842 rc = VERR_INVALID_PARAMETER;
2843 break;
2844 }
2845 rc = crVBoxServerClientGet(u32ClientID, &pClient);
2846 if (RT_FAILURE(rc))
2847 {
2848 pHdr->result = rc;
2849 crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
2850 return rc;
2851 }
2852
2853 /* This should never fire unless we start to multithread */
2854 CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0);
2855 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2856
2857 pClient->conn->pBuffer = pBuffer;
2858 pClient->conn->cbBuffer = cbBuffer;
2859 CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback);
2860 rc = crVBoxServerInternalClientWriteRead(pClient);
2861 CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData);
2862 return rc;
2863 }
2864
2865 crWarning("invalid number of args");
2866 rc = VERR_INVALID_PARAMETER;
2867 break;
2868 }
2869
2870 case SHCRGL_GUEST_FN_SET_VERSION:
2871 {
2872 crWarning("invalid function");
2873 rc = VERR_NOT_IMPLEMENTED;
2874 break;
2875 }
2876
2877 case SHCRGL_GUEST_FN_SET_PID:
2878 {
2879 crWarning("invalid function");
2880 rc = VERR_NOT_IMPLEMENTED;
2881 break;
2882 }
2883
2884 default:
2885 {
2886 crWarning("invalid function");
2887 rc = VERR_NOT_IMPLEMENTED;
2888 break;
2889 }
2890
2891 }
2892
2893 /* we can be on fail only here */
2894 CRASSERT(RT_FAILURE(rc));
2895 pHdr->result = rc;
2896 crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS);
2897 return rc;
2898}
2899
2900int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t cbCtl)
2901{
2902 int rc = VINF_SUCCESS;
2903
2904 switch (pCtl->enmType)
2905 {
2906 case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP:
2907 {
2908 PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)pCtl;
2909 g_pvVRamBase = (uint8_t*)pSetup->pvVRamBase;
2910 g_cbVRam = pSetup->cbVRam;
2911 rc = VINF_SUCCESS;
2912 break;
2913 }
2914 case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN:
2915 case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END:
2916 rc = VINF_SUCCESS;
2917 break;
2918 case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION:
2919 {
2920 PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION)pCtl;
2921 g_hCrHgsmiCompletion = pSetup->hCompletion;
2922 g_pfnCrHgsmiCompletion = pSetup->pfnCompletion;
2923 rc = VINF_SUCCESS;
2924 break;
2925 }
2926 default:
2927 AssertMsgFailed(("invalid param %d", pCtl->enmType));
2928 rc = VERR_INVALID_PARAMETER;
2929 }
2930
2931 /* NOTE: Control commands can NEVER be pended here, this is why its a task of a caller (Main)
2932 * to complete them accordingly.
2933 * This approach allows using host->host and host->guest commands in the same way here
2934 * making the command completion to be the responsibility of the command originator.
2935 * E.g. ctl commands can be both Hgcm Host synchronous commands that do not require completion at all,
2936 * or Hgcm Host Fast Call commands that do require completion. All this details are hidden here */
2937 return rc;
2938}
2939#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette