VirtualBox

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

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

crOpenGL: Seamles+3D bufixes

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