VirtualBox

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

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

crOpenGL: more correct saved state backwards compatibility

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