VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DisplayImpl.cpp@ 50394

Last change on this file since 50394 was 50394, checked in by vboxsync, 11 years ago

crOpenGL: greyed-out 3D image on VM pause

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 167.0 KB
Line 
1/* $Id: DisplayImpl.cpp 50394 2014-02-10 15:33:47Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "DisplayImpl.h"
19#include "DisplayUtils.h"
20#include "ConsoleImpl.h"
21#include "ConsoleVRDPServer.h"
22#include "VMMDev.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27/* generated header */
28#include "VBoxEvents.h"
29
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/asm.h>
33#include <iprt/time.h>
34#include <iprt/cpp/utils.h>
35
36#include <VBox/vmm/pdmdrv.h>
37#if defined(DEBUG) || defined(VBOX_STRICT) /* for VM_ASSERT_EMT(). */
38# include <VBox/vmm/vm.h>
39#endif
40
41#ifdef VBOX_WITH_VIDEOHWACCEL
42# include <VBox/VBoxVideo.h>
43#endif
44
45#if defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_CRHGSMI)
46# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
47#endif
48
49#include <VBox/com/array.h>
50
51#ifdef VBOX_WITH_VPX
52# include <iprt/path.h>
53# include "VideoRec.h"
54#endif
55
56#ifdef VBOX_WITH_CROGL
57typedef enum
58{
59 CRVREC_STATE_IDLE,
60 CRVREC_STATE_SUBMITTED
61} CRVREC_STATE;
62#endif
63
64/**
65 * Display driver instance data.
66 *
67 * @implements PDMIDISPLAYCONNECTOR
68 */
69typedef struct DRVMAINDISPLAY
70{
71 /** Pointer to the display object. */
72 Display *pDisplay;
73 /** Pointer to the driver instance structure. */
74 PPDMDRVINS pDrvIns;
75 /** Pointer to the keyboard port interface of the driver/device above us. */
76 PPDMIDISPLAYPORT pUpPort;
77 /** Our display connector interface. */
78 PDMIDISPLAYCONNECTOR IConnector;
79#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
80 /** VBVA callbacks */
81 PPDMIDISPLAYVBVACALLBACKS pVBVACallbacks;
82#endif
83} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
84
85/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
86#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
87
88#ifdef DEBUG_sunlover
89static STAMPROFILE g_StatDisplayRefresh;
90static int g_stam = 0;
91#endif /* DEBUG_sunlover */
92
93// constructor / destructor
94/////////////////////////////////////////////////////////////////////////////
95
96Display::Display()
97 : mParent(NULL)
98{
99}
100
101Display::~Display()
102{
103}
104
105
106HRESULT Display::FinalConstruct()
107{
108 mpVbvaMemory = NULL;
109 mfVideoAccelEnabled = false;
110 mfVideoAccelVRDP = false;
111 mfu32SupportedOrders = 0;
112 mcVideoAccelVRDPRefs = 0;
113
114 mpPendingVbvaMemory = NULL;
115 mfPendingVideoAccelEnable = false;
116
117 mfMachineRunning = false;
118#ifdef VBOX_WITH_CROGL
119 mfCrOglDataHidden = false;
120#endif
121
122 mpu8VbvaPartial = NULL;
123 mcbVbvaPartial = 0;
124
125 mpDrv = NULL;
126 mpVMMDev = NULL;
127 mfVMMDevInited = false;
128
129 mLastAddress = NULL;
130 mLastBytesPerLine = 0;
131 mLastBitsPerPixel = 0,
132 mLastWidth = 0;
133 mLastHeight = 0;
134
135 int rc = RTCritSectInit(&mVBVALock);
136 AssertRC(rc);
137
138 rc = RTCritSectInit(&mSaveSeamlessRectLock);
139 AssertRC(rc);
140
141 mfu32PendingVideoAccelDisable = false;
142
143#ifdef VBOX_WITH_HGSMI
144 mu32UpdateVBVAFlags = 0;
145#endif
146#ifdef VBOX_WITH_VPX
147 mpVideoRecCtx = NULL;
148 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
149 maVideoRecEnabled[i] = true;
150#endif
151
152#ifdef VBOX_WITH_CRHGSMI
153 mhCrOglSvc = NULL;
154#endif
155#ifdef VBOX_WITH_CROGL
156 RT_ZERO(mCrOglCallbacks);
157 RT_ZERO(mCrOglScreenshotData);
158 mfCrOglVideoRecState = CRVREC_STATE_IDLE;
159 mCrOglScreenshotData.u32Screen = CRSCREEN_ALL;
160 mCrOglScreenshotData.pvContext = this;
161 mCrOglScreenshotData.pfnScreenshotBegin = displayCrVRecScreenshotBegin;
162 mCrOglScreenshotData.pfnScreenshotPerform = displayCrVRecScreenshotPerform;
163 mCrOglScreenshotData.pfnScreenshotEnd = displayCrVRecScreenshotEnd;
164#endif
165
166 return BaseFinalConstruct();
167}
168
169void Display::FinalRelease()
170{
171 uninit();
172
173 if (RTCritSectIsInitialized (&mVBVALock))
174 {
175 RTCritSectDelete (&mVBVALock);
176 RT_ZERO(mVBVALock);
177 }
178
179 if (RTCritSectIsInitialized(&mSaveSeamlessRectLock))
180 {
181 RTCritSectDelete(&mSaveSeamlessRectLock);
182 RT_ZERO(mSaveSeamlessRectLock);
183 }
184 BaseFinalRelease();
185}
186
187// public initializer/uninitializer for internal purposes only
188/////////////////////////////////////////////////////////////////////////////
189
190#define kMaxSizeThumbnail 64
191
192/**
193 * Save thumbnail and screenshot of the guest screen.
194 */
195static int displayMakeThumbnail(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
196 uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
197{
198 int rc = VINF_SUCCESS;
199
200 uint8_t *pu8Thumbnail = NULL;
201 uint32_t cbThumbnail = 0;
202 uint32_t cxThumbnail = 0;
203 uint32_t cyThumbnail = 0;
204
205 if (cx > cy)
206 {
207 cxThumbnail = kMaxSizeThumbnail;
208 cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
209 }
210 else
211 {
212 cyThumbnail = kMaxSizeThumbnail;
213 cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
214 }
215
216 LogRelFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
217
218 cbThumbnail = cxThumbnail * 4 * cyThumbnail;
219 pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
220
221 if (pu8Thumbnail)
222 {
223 uint8_t *dst = pu8Thumbnail;
224 uint8_t *src = pu8Data;
225 int dstW = cxThumbnail;
226 int dstH = cyThumbnail;
227 int srcW = cx;
228 int srcH = cy;
229 int iDeltaLine = cx * 4;
230
231 BitmapScale32 (dst,
232 dstW, dstH,
233 src,
234 iDeltaLine,
235 srcW, srcH);
236
237 *ppu8Thumbnail = pu8Thumbnail;
238 *pcbThumbnail = cbThumbnail;
239 *pcxThumbnail = cxThumbnail;
240 *pcyThumbnail = cyThumbnail;
241 }
242 else
243 {
244 rc = VERR_NO_MEMORY;
245 }
246
247 return rc;
248}
249
250DECLCALLBACK(void)
251Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
252{
253 Display *that = static_cast<Display*>(pvUser);
254
255 /* 32bpp small RGB image. */
256 uint8_t *pu8Thumbnail = NULL;
257 uint32_t cbThumbnail = 0;
258 uint32_t cxThumbnail = 0;
259 uint32_t cyThumbnail = 0;
260
261 /* PNG screenshot. */
262 uint8_t *pu8PNG = NULL;
263 uint32_t cbPNG = 0;
264 uint32_t cxPNG = 0;
265 uint32_t cyPNG = 0;
266
267 Console::SafeVMPtr ptrVM(that->mParent);
268 if (ptrVM.isOk())
269 {
270 /* Query RGB bitmap. */
271 uint8_t *pu8Data = NULL;
272 size_t cbData = 0;
273 uint32_t cx = 0;
274 uint32_t cy = 0;
275
276 /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
277 int rc = Display::displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pu8Data, &cbData, &cx, &cy);
278
279 /*
280 * It is possible that success is returned but everything is 0 or NULL.
281 * (no display attached if a VM is running with VBoxHeadless on OSE for example)
282 */
283 if (RT_SUCCESS(rc) && pu8Data)
284 {
285 Assert(cx && cy);
286
287 /* Prepare a small thumbnail and a PNG screenshot. */
288 displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
289 rc = DisplayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
290 if (RT_FAILURE(rc))
291 {
292 if (pu8PNG)
293 {
294 RTMemFree(pu8PNG);
295 pu8PNG = NULL;
296 }
297 cbPNG = 0;
298 cxPNG = 0;
299 cyPNG = 0;
300 }
301
302 /* This can be called from any thread. */
303 that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pu8Data);
304 }
305 }
306 else
307 {
308 LogFunc(("Failed to get VM pointer 0x%x\n", ptrVM.rc()));
309 }
310
311 /* Regardless of rc, save what is available:
312 * Data format:
313 * uint32_t cBlocks;
314 * [blocks]
315 *
316 * Each block is:
317 * uint32_t cbBlock; if 0 - no 'block data'.
318 * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
319 * [block data]
320 *
321 * Block data for bitmap and PNG:
322 * uint32_t cx;
323 * uint32_t cy;
324 * [image data]
325 */
326 SSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
327
328 /* First block. */
329 SSMR3PutU32(pSSM, cbThumbnail + 2 * sizeof (uint32_t));
330 SSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
331
332 if (cbThumbnail)
333 {
334 SSMR3PutU32(pSSM, cxThumbnail);
335 SSMR3PutU32(pSSM, cyThumbnail);
336 SSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
337 }
338
339 /* Second block. */
340 SSMR3PutU32(pSSM, cbPNG + 2 * sizeof (uint32_t));
341 SSMR3PutU32(pSSM, 1); /* Block type: png. */
342
343 if (cbPNG)
344 {
345 SSMR3PutU32(pSSM, cxPNG);
346 SSMR3PutU32(pSSM, cyPNG);
347 SSMR3PutMem(pSSM, pu8PNG, cbPNG);
348 }
349
350 RTMemFree(pu8PNG);
351 RTMemFree(pu8Thumbnail);
352}
353
354DECLCALLBACK(int)
355Display::displaySSMLoadScreenshot(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
356{
357 Display *that = static_cast<Display*>(pvUser);
358
359 if (uVersion != sSSMDisplayScreenshotVer)
360 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
361 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
362
363 /* Skip data. */
364 uint32_t cBlocks;
365 int rc = SSMR3GetU32(pSSM, &cBlocks);
366 AssertRCReturn(rc, rc);
367
368 for (uint32_t i = 0; i < cBlocks; i++)
369 {
370 uint32_t cbBlock;
371 rc = SSMR3GetU32(pSSM, &cbBlock);
372 AssertRCBreak(rc);
373
374 uint32_t typeOfBlock;
375 rc = SSMR3GetU32(pSSM, &typeOfBlock);
376 AssertRCBreak(rc);
377
378 LogRelFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
379
380 /* Note: displaySSMSaveScreenshot writes size of a block = 8 and
381 * do not write any data if the image size was 0.
382 * @todo Fix and increase saved state version.
383 */
384 if (cbBlock > 2 * sizeof (uint32_t))
385 {
386 rc = SSMR3Skip(pSSM, cbBlock);
387 AssertRCBreak(rc);
388 }
389 }
390
391 return rc;
392}
393
394/**
395 * Save/Load some important guest state
396 */
397DECLCALLBACK(void)
398Display::displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
399{
400 Display *that = static_cast<Display*>(pvUser);
401
402 SSMR3PutU32(pSSM, that->mcMonitors);
403 for (unsigned i = 0; i < that->mcMonitors; i++)
404 {
405 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
406 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
407 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
408 SSMR3PutU32(pSSM, that->maFramebuffers[i].w);
409 SSMR3PutU32(pSSM, that->maFramebuffers[i].h);
410 SSMR3PutS32(pSSM, that->maFramebuffers[i].xOrigin);
411 SSMR3PutS32(pSSM, that->maFramebuffers[i].yOrigin);
412 SSMR3PutU32(pSSM, that->maFramebuffers[i].flags);
413 }
414}
415
416DECLCALLBACK(int)
417Display::displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
418{
419 Display *that = static_cast<Display*>(pvUser);
420
421 if (!( uVersion == sSSMDisplayVer
422 || uVersion == sSSMDisplayVer2
423 || uVersion == sSSMDisplayVer3))
424 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
425 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
426
427 uint32_t cMonitors;
428 int rc = SSMR3GetU32(pSSM, &cMonitors);
429 if (cMonitors != that->mcMonitors)
430 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, that->mcMonitors);
431
432 for (uint32_t i = 0; i < cMonitors; i++)
433 {
434 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
435 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
436 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
437 if ( uVersion == sSSMDisplayVer2
438 || uVersion == sSSMDisplayVer3)
439 {
440 uint32_t w;
441 uint32_t h;
442 SSMR3GetU32(pSSM, &w);
443 SSMR3GetU32(pSSM, &h);
444 that->maFramebuffers[i].w = w;
445 that->maFramebuffers[i].h = h;
446 }
447 if (uVersion == sSSMDisplayVer3)
448 {
449 int32_t xOrigin;
450 int32_t yOrigin;
451 uint32_t flags;
452 SSMR3GetS32(pSSM, &xOrigin);
453 SSMR3GetS32(pSSM, &yOrigin);
454 SSMR3GetU32(pSSM, &flags);
455 that->maFramebuffers[i].xOrigin = xOrigin;
456 that->maFramebuffers[i].yOrigin = yOrigin;
457 that->maFramebuffers[i].flags = (uint16_t)flags;
458 that->maFramebuffers[i].fDisabled = (that->maFramebuffers[i].flags & VBVA_SCREEN_F_DISABLED) != 0;
459 }
460 }
461
462 return VINF_SUCCESS;
463}
464
465/**
466 * Initializes the display object.
467 *
468 * @returns COM result indicator
469 * @param parent handle of our parent object
470 * @param qemuConsoleData address of common console data structure
471 */
472HRESULT Display::init(Console *aParent)
473{
474 ComAssertRet(aParent, E_INVALIDARG);
475 /* Enclose the state transition NotReady->InInit->Ready */
476 AutoInitSpan autoInitSpan(this);
477 AssertReturn(autoInitSpan.isOk(), E_FAIL);
478
479 unconst(mParent) = aParent;
480
481 ULONG ul;
482 mParent->machine()->COMGETTER(MonitorCount)(&ul);
483 mcMonitors = ul;
484
485 for (ul = 0; ul < mcMonitors; ul++)
486 {
487 maFramebuffers[ul].u32Offset = 0;
488 maFramebuffers[ul].u32MaxFramebufferSize = 0;
489 maFramebuffers[ul].u32InformationSize = 0;
490
491 maFramebuffers[ul].pFramebuffer = NULL;
492 /* All secondary monitors are disabled at startup. */
493 maFramebuffers[ul].fDisabled = ul > 0;
494
495 maFramebuffers[ul].xOrigin = 0;
496 maFramebuffers[ul].yOrigin = 0;
497
498 maFramebuffers[ul].w = 0;
499 maFramebuffers[ul].h = 0;
500
501 maFramebuffers[ul].flags = maFramebuffers[ul].fDisabled? VBVA_SCREEN_F_DISABLED: 0;
502
503 maFramebuffers[ul].u16BitsPerPixel = 0;
504 maFramebuffers[ul].pu8FramebufferVRAM = NULL;
505 maFramebuffers[ul].u32LineSize = 0;
506
507 maFramebuffers[ul].pHostEvents = NULL;
508
509 maFramebuffers[ul].u32ResizeStatus = ResizeStatus_Void;
510
511 maFramebuffers[ul].fDefaultFormat = false;
512
513 maFramebuffers[ul].mcSavedVisibleRegion = 0;
514 maFramebuffers[ul].mpSavedVisibleRegion = NULL;
515
516 RT_ZERO(maFramebuffers[ul].dirtyRect);
517 RT_ZERO(maFramebuffers[ul].pendingResize);
518#ifdef VBOX_WITH_HGSMI
519 maFramebuffers[ul].fVBVAEnabled = false;
520 maFramebuffers[ul].fVBVAForceResize = false;
521 maFramebuffers[ul].cVBVASkipUpdate = 0;
522 RT_ZERO(maFramebuffers[ul].vbvaSkippedRect);
523 maFramebuffers[ul].pVBVAHostFlags = NULL;
524#endif /* VBOX_WITH_HGSMI */
525#ifdef VBOX_WITH_CROGL
526 RT_ZERO(maFramebuffers[ul].pendingViewportInfo);
527#endif
528 }
529
530 {
531 // register listener for state change events
532 ComPtr<IEventSource> es;
533 mParent->COMGETTER(EventSource)(es.asOutParam());
534 com::SafeArray <VBoxEventType_T> eventTypes;
535 eventTypes.push_back(VBoxEventType_OnStateChanged);
536 es->RegisterListener(this, ComSafeArrayAsInParam(eventTypes), true);
537 }
538
539 /* Confirm a successful initialization */
540 autoInitSpan.setSucceeded();
541
542 return S_OK;
543}
544
545/**
546 * Uninitializes the instance and sets the ready flag to FALSE.
547 * Called either from FinalRelease() or by the parent when it gets destroyed.
548 */
549void Display::uninit()
550{
551 LogRelFlowFunc(("this=%p\n", this));
552
553 /* Enclose the state transition Ready->InUninit->NotReady */
554 AutoUninitSpan autoUninitSpan(this);
555 if (autoUninitSpan.uninitDone())
556 return;
557
558 ULONG ul;
559 for (ul = 0; ul < mcMonitors; ul++)
560 maFramebuffers[ul].pFramebuffer = NULL;
561
562 if (mParent)
563 {
564 ComPtr<IEventSource> es;
565 mParent->COMGETTER(EventSource)(es.asOutParam());
566 es->UnregisterListener(this);
567 }
568
569 unconst(mParent) = NULL;
570
571 if (mpDrv)
572 mpDrv->pDisplay = NULL;
573
574 mpDrv = NULL;
575 mpVMMDev = NULL;
576 mfVMMDevInited = true;
577}
578
579/**
580 * Register the SSM methods. Called by the power up thread to be able to
581 * pass pVM
582 */
583int Display::registerSSM(PUVM pUVM)
584{
585 /* Version 2 adds width and height of the framebuffer; version 3 adds
586 * the framebuffer offset in the virtual desktop and the framebuffer flags.
587 */
588 int rc = SSMR3RegisterExternal(pUVM, "DisplayData", 0, sSSMDisplayVer3,
589 mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
590 NULL, NULL, NULL,
591 NULL, displaySSMSave, NULL,
592 NULL, displaySSMLoad, NULL, this);
593 AssertRCReturn(rc, rc);
594
595 /*
596 * Register loaders for old saved states where iInstance was
597 * 3 * sizeof(uint32_t *) due to a code mistake.
598 */
599 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
600 NULL, NULL, NULL,
601 NULL, NULL, NULL,
602 NULL, displaySSMLoad, NULL, this);
603 AssertRCReturn(rc, rc);
604
605 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
606 NULL, NULL, NULL,
607 NULL, NULL, NULL,
608 NULL, displaySSMLoad, NULL, this);
609 AssertRCReturn(rc, rc);
610
611 /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
612 rc = SSMR3RegisterExternal(pUVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
613 NULL, NULL, NULL,
614 NULL, displaySSMSaveScreenshot, NULL,
615 NULL, displaySSMLoadScreenshot, NULL, this);
616
617 AssertRCReturn(rc, rc);
618
619 return VINF_SUCCESS;
620}
621
622#ifdef VBOX_WITH_CROGL
623int Display::crOglWindowsShow(bool fShow)
624{
625 if (!mfCrOglDataHidden == !!fShow)
626 return VINF_SUCCESS;
627
628 VMMDev *pVMMDev = mParent->getVMMDev();
629 if (!pVMMDev)
630 {
631 AssertMsgFailed(("no vmmdev\n"));
632 return VERR_INVALID_STATE;
633 }
634
635 if (!mhCrOglSvc)
636 {
637 AssertMsgFailed(("no mhCrOglSvc\n"));
638 return VERR_INVALID_STATE;
639 }
640
641 VBOXHGCMSVCPARM parm;
642
643 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
644 parm.u.uint32 = (uint32_t)fShow;
645
646 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_WINDOWS_SHOW, &parm, NULL, NULL);
647 if (RT_SUCCESS(rc))
648 mfCrOglDataHidden = !fShow;
649 else
650 AssertMsgFailed(("hgcmHostFastCallAsync failed rc %n", rc));
651
652 return rc;
653}
654#endif
655
656
657// IEventListener method
658STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
659{
660 VBoxEventType_T aType = VBoxEventType_Invalid;
661
662 aEvent->COMGETTER(Type)(&aType);
663 switch (aType)
664 {
665 case VBoxEventType_OnStateChanged:
666 {
667 ComPtr<IStateChangedEvent> scev = aEvent;
668 Assert(scev);
669 MachineState_T machineState;
670 scev->COMGETTER(State)(&machineState);
671 if ( machineState == MachineState_Running
672 || machineState == MachineState_Teleporting
673 || machineState == MachineState_LiveSnapshotting
674 )
675 {
676 LogRelFlowFunc(("Machine is running.\n"));
677
678 mfMachineRunning = true;
679
680#ifdef VBOX_WITH_CROGL
681 crOglWindowsShow(true);
682#endif
683 }
684 else
685 {
686 mfMachineRunning = false;
687
688#ifdef VBOX_WITH_CROGL
689 if (machineState == MachineState_Paused)
690 crOglWindowsShow(false);
691#endif
692 }
693 break;
694 }
695 default:
696 AssertFailed();
697 }
698
699 return S_OK;
700}
701
702// public methods only for internal purposes
703/////////////////////////////////////////////////////////////////////////////
704
705/**
706 * @thread EMT
707 */
708static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
709 ULONG pixelFormat, void *pvVRAM,
710 uint32_t bpp, uint32_t cbLine,
711 uint32_t w, uint32_t h)
712{
713 Assert (pFramebuffer);
714
715 /* Call the framebuffer to try and set required pixelFormat. */
716 BOOL finished = TRUE;
717
718 pFramebuffer->RequestResize (uScreenId, pixelFormat, (BYTE *) pvVRAM,
719 bpp, cbLine, w, h, &finished);
720
721 if (!finished)
722 {
723 LogRelFlowFunc(("External framebuffer wants us to wait!\n"));
724 return VINF_VGA_RESIZE_IN_PROGRESS;
725 }
726
727 return VINF_SUCCESS;
728}
729
730int Display::notifyCroglResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
731{
732#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
733 BOOL is3denabled;
734 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
735
736 if (is3denabled)
737 {
738 int rc = VERR_INVALID_STATE;
739 if (mhCrOglSvc)
740 {
741 VMMDev *pVMMDev = mParent->getVMMDev();
742 if (pVMMDev)
743 {
744 CRVBOXHGCMDEVRESIZE *pData = (CRVBOXHGCMDEVRESIZE*)RTMemAlloc(sizeof (*pData));
745 if (pData)
746 {
747 pData->Screen = *pScreen;
748 pData->pvVRAM = pvVRAM;
749
750 VBOXHGCMSVCPARM parm;
751
752 parm.type = VBOX_HGCM_SVC_PARM_PTR;
753 parm.u.pointer.addr = pData;
754 parm.u.pointer.size = sizeof (*pData);
755
756 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_DEV_RESIZE, &parm, displayCrAsyncCmdCompletion, this);
757 AssertRC(rc);
758 }
759 else
760 rc = VERR_NO_MEMORY;
761 }
762 }
763
764 return rc;
765 }
766#endif /* #if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
767 return VINF_SUCCESS;
768}
769
770/**
771 * Handles display resize event.
772 * Disables access to VGA device;
773 * calls the framebuffer RequestResize method;
774 * if framebuffer resizes synchronously,
775 * updates the display connector data and enables access to the VGA device.
776 *
777 * @param w New display width
778 * @param h New display height
779 *
780 * @thread EMT
781 */
782int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
783 uint32_t cbLine, uint32_t w, uint32_t h, uint16_t flags)
784{
785 LogRel(("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
786 "w=%d h=%d bpp=%d cbLine=0x%X, flags=0x%X\n",
787 uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
788
789 /* If there is no framebuffer, this call is not interesting. */
790 if ( uScreenId >= mcMonitors
791 || maFramebuffers[uScreenId].pFramebuffer.isNull())
792 {
793 return VINF_SUCCESS;
794 }
795
796 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
797 {
798 mLastAddress = pvVRAM;
799 mLastBytesPerLine = cbLine;
800 mLastBitsPerPixel = bpp;
801 mLastWidth = w;
802 mLastHeight = h;
803 mLastFlags = flags;
804 }
805
806 ULONG pixelFormat;
807
808 switch (bpp)
809 {
810 case 32:
811 case 24:
812 case 16:
813 pixelFormat = FramebufferPixelFormat_FOURCC_RGB;
814 break;
815 default:
816 pixelFormat = FramebufferPixelFormat_Opaque;
817 bpp = cbLine = 0;
818 break;
819 }
820
821 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
822 * disable access to the VGA device by the EMT thread.
823 */
824 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
825 ResizeStatus_InProgress, ResizeStatus_Void);
826 if (!f)
827 {
828 /* This could be a result of the screenshot taking call Display::TakeScreenShot:
829 * if the framebuffer is processing the resize request and GUI calls the TakeScreenShot
830 * and the guest has reprogrammed the virtual VGA devices again so a new resize is required.
831 *
832 * Save the resize information and return the pending status code.
833 *
834 * Note: the resize information is only accessed on EMT so no serialization is required.
835 */
836 LogRel(("Display::handleDisplayResize(): Warning: resize postponed.\n"));
837
838 maFramebuffers[uScreenId].pendingResize.fPending = true;
839 maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
840 maFramebuffers[uScreenId].pendingResize.pvVRAM = pvVRAM;
841 maFramebuffers[uScreenId].pendingResize.bpp = bpp;
842 maFramebuffers[uScreenId].pendingResize.cbLine = cbLine;
843 maFramebuffers[uScreenId].pendingResize.w = w;
844 maFramebuffers[uScreenId].pendingResize.h = h;
845 maFramebuffers[uScreenId].pendingResize.flags = flags;
846
847 return VINF_VGA_RESIZE_IN_PROGRESS;
848 }
849
850 /* Framebuffer will be invalid during resize, make sure that it is not accessed. */
851 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
852 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, false);
853
854 int rc = callFramebufferResize (maFramebuffers[uScreenId].pFramebuffer, uScreenId,
855 pixelFormat, pvVRAM, bpp, cbLine, w, h);
856 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
857 {
858 /* Immediately return to the caller. ResizeCompleted will be called back by the
859 * GUI thread. The ResizeCompleted callback will change the resize status from
860 * InProgress to UpdateDisplayData. The latter status will be checked by the
861 * display timer callback on EMT and all required adjustments will be done there.
862 */
863 return rc;
864 }
865
866 /* Set the status so the 'handleResizeCompleted' would work. */
867 f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
868 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
869 AssertRelease(f);NOREF(f);
870
871 AssertRelease(!maFramebuffers[uScreenId].pendingResize.fPending);
872
873 /* The method also unlocks the framebuffer. */
874 handleResizeCompletedEMT();
875
876 return VINF_SUCCESS;
877}
878
879/**
880 * Framebuffer has been resized.
881 * Read the new display data and unlock the framebuffer.
882 *
883 * @thread EMT
884 */
885void Display::handleResizeCompletedEMT (void)
886{
887 LogRelFlowFunc(("\n"));
888
889 unsigned uScreenId;
890 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
891 {
892 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
893
894 /* Try to into non resizing state. */
895 bool f = ASMAtomicCmpXchgU32 (&pFBInfo->u32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
896
897 if (f == false)
898 {
899 /* This is not the display that has completed resizing. */
900 continue;
901 }
902
903 /* Check whether a resize is pending for this framebuffer. */
904 if (pFBInfo->pendingResize.fPending)
905 {
906 /* Reset the condition, call the display resize with saved data and continue.
907 *
908 * Note: handleDisplayResize can call handleResizeCompletedEMT back,
909 * but infinite recursion is not possible, because when the handleResizeCompletedEMT
910 * is called, the pFBInfo->pendingResize.fPending is equal to false.
911 */
912 pFBInfo->pendingResize.fPending = false;
913 handleDisplayResize (uScreenId, pFBInfo->pendingResize.bpp, pFBInfo->pendingResize.pvVRAM,
914 pFBInfo->pendingResize.cbLine, pFBInfo->pendingResize.w, pFBInfo->pendingResize.h, pFBInfo->pendingResize.flags);
915 continue;
916 }
917
918 /* Inform VRDP server about the change of display parameters.
919 * Must be done before calling NotifyUpdate below.
920 */
921 LogRelFlowFunc(("Calling VRDP\n"));
922 mParent->consoleVRDPServer()->SendResize();
923
924 /* @todo Merge these two 'if's within one 'if (!pFBInfo->pFramebuffer.isNull())' */
925 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
926 {
927 /* Primary framebuffer has completed the resize. Update the connector data for VGA device. */
928 int rc2 = updateDisplayData();
929
930 /* Check the framebuffer pixel format to setup the rendering in VGA device. */
931 BOOL usesGuestVRAM = FALSE;
932 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
933
934 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
935
936 /* If the primary framebuffer is disabled, tell the VGA device to not to copy
937 * pixels from VRAM to the framebuffer.
938 */
939 if (pFBInfo->fDisabled || RT_FAILURE(rc2))
940 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, false);
941 else
942 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort,
943 pFBInfo->fDefaultFormat);
944
945 /* If the screen resize was because of disabling, tell framebuffer to repaint.
946 * The framebuffer if now in default format so it will not use guest VRAM
947 * and will show usually black image which is there after framebuffer resize.
948 */
949 if (pFBInfo->fDisabled)
950 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
951 }
952 else if (!pFBInfo->pFramebuffer.isNull())
953 {
954 BOOL usesGuestVRAM = FALSE;
955 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
956
957 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
958
959 /* If the screen resize was because of disabling, tell framebuffer to repaint.
960 * The framebuffer if now in default format so it will not use guest VRAM
961 * and will show usually black image which is there after framebuffer resize.
962 */
963 if (pFBInfo->fDisabled)
964 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, pFBInfo->w, pFBInfo->h);
965 }
966 LogRelFlow(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
967
968 /* Handle the case if there are some saved visible region that needs to be
969 * applied after the resize of the framebuffer is completed
970 */
971 SaveSeamlessRectLock();
972 PRTRECT pSavedVisibleRegion = pFBInfo->mpSavedVisibleRegion;
973 uint32_t cSavedVisibleRegion = pFBInfo->mcSavedVisibleRegion;
974 pFBInfo->mpSavedVisibleRegion = NULL;
975 pFBInfo->mcSavedVisibleRegion = 0;
976 SaveSeamlessRectUnLock();
977
978 if (pSavedVisibleRegion)
979 {
980 handleSetVisibleRegion(cSavedVisibleRegion, pSavedVisibleRegion);
981 RTMemFree(pSavedVisibleRegion);
982 }
983
984#ifdef DEBUG_sunlover
985 if (!g_stam)
986 {
987 Console::SafeVMPtr ptrVM(mParent);
988 AssertComRC(ptrVM.rc());
989 STAMR3RegisterU(ptrVM.rawUVM(), &g_StatDisplayRefresh, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
990 "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
991 g_stam = 1;
992 }
993#endif /* DEBUG_sunlover */
994
995#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
996 {
997 BOOL is3denabled;
998 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
999
1000 if (is3denabled)
1001 {
1002 VBOXHGCMSVCPARM parm;
1003
1004 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
1005 parm.u.uint32 = uScreenId;
1006
1007 VMMDev *pVMMDev = mParent->getVMMDev();
1008 if (pVMMDev)
1009 {
1010#if 0
1011 if (mhCrOglSvc)
1012 pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SCREEN_CHANGED, &parm, NULL, NULL);
1013 else
1014 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
1015#else
1016 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
1017#endif
1018 }
1019 }
1020 }
1021#endif /* VBOX_WITH_CROGL */
1022 }
1023}
1024
1025static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
1026{
1027 /* Correct negative x and y coordinates. */
1028 if (*px < 0)
1029 {
1030 *px += *pw; /* Compute xRight which is also the new width. */
1031
1032 *pw = (*px < 0)? 0: *px;
1033
1034 *px = 0;
1035 }
1036
1037 if (*py < 0)
1038 {
1039 *py += *ph; /* Compute xBottom, which is also the new height. */
1040
1041 *ph = (*py < 0)? 0: *py;
1042
1043 *py = 0;
1044 }
1045
1046 /* Also check if coords are greater than the display resolution. */
1047 if (*px + *pw > cx)
1048 {
1049 *pw = cx > *px? cx - *px: 0;
1050 }
1051
1052 if (*py + *ph > cy)
1053 {
1054 *ph = cy > *py? cy - *py: 0;
1055 }
1056}
1057
1058unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
1059{
1060 DISPLAYFBINFO *pInfo = pInfos;
1061 unsigned uScreenId;
1062 LogSunlover(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
1063 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
1064 {
1065 LogSunlover((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
1066 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
1067 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
1068 {
1069 /* The rectangle belongs to the screen. Correct coordinates. */
1070 *px -= pInfo->xOrigin;
1071 *py -= pInfo->yOrigin;
1072 LogSunlover((" -> %d,%d", *px, *py));
1073 break;
1074 }
1075 }
1076 if (uScreenId == cInfos)
1077 {
1078 /* Map to primary screen. */
1079 uScreenId = 0;
1080 }
1081 LogSunlover((" scr %d\n", uScreenId));
1082 return uScreenId;
1083}
1084
1085
1086/**
1087 * Handles display update event.
1088 *
1089 * @param x Update area x coordinate
1090 * @param y Update area y coordinate
1091 * @param w Update area width
1092 * @param h Update area height
1093 *
1094 * @thread EMT
1095 */
1096void Display::handleDisplayUpdateLegacy (int x, int y, int w, int h)
1097{
1098 unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1099
1100#ifdef DEBUG_sunlover
1101 LogFlowFunc(("%d,%d %dx%d (checked)\n", x, y, w, h));
1102#endif /* DEBUG_sunlover */
1103
1104 handleDisplayUpdate (uScreenId, x, y, w, h);
1105}
1106
1107void Display::handleDisplayUpdate (unsigned uScreenId, int x, int y, int w, int h)
1108{
1109 /*
1110 * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
1111 * Safe to use VBVA vars and take the framebuffer lock.
1112 */
1113
1114#ifdef DEBUG_sunlover
1115 LogFlowFunc(("[%d] %d,%d %dx%d (%d,%d)\n",
1116 uScreenId, x, y, w, h, mpDrv->IConnector.cx, mpDrv->IConnector.cy));
1117#endif /* DEBUG_sunlover */
1118
1119 IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
1120
1121 // if there is no framebuffer, this call is not interesting
1122 if ( pFramebuffer == NULL
1123 || maFramebuffers[uScreenId].fDisabled)
1124 return;
1125
1126 pFramebuffer->Lock();
1127
1128 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1129 checkCoordBounds (&x, &y, &w, &h, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
1130 else
1131 checkCoordBounds (&x, &y, &w, &h, maFramebuffers[uScreenId].w,
1132 maFramebuffers[uScreenId].h);
1133
1134 if (w != 0 && h != 0)
1135 pFramebuffer->NotifyUpdate(x, y, w, h);
1136
1137 pFramebuffer->Unlock();
1138
1139#ifndef VBOX_WITH_HGSMI
1140 if (!mfVideoAccelEnabled)
1141 {
1142#else
1143 if (!mfVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
1144 {
1145#endif /* VBOX_WITH_HGSMI */
1146 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
1147 * Inform the server here only if VBVA is disabled.
1148 */
1149 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1150 mParent->consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
1151 }
1152}
1153
1154/**
1155 * Returns the upper left and lower right corners of the virtual framebuffer.
1156 * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
1157 * and the origin is (0, 0), not (1, 1) like the GUI returns.
1158 */
1159void Display::getFramebufferDimensions(int32_t *px1, int32_t *py1,
1160 int32_t *px2, int32_t *py2)
1161{
1162 int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 AssertPtrReturnVoid(px1);
1166 AssertPtrReturnVoid(py1);
1167 AssertPtrReturnVoid(px2);
1168 AssertPtrReturnVoid(py2);
1169 LogRelFlowFunc(("\n"));
1170
1171 if (!mpDrv)
1172 return;
1173 /* If VBVA is not in use then this flag will not be set and this
1174 * will still work as it should. */
1175 if (!maFramebuffers[0].fDisabled)
1176 {
1177 x1 = (int32_t)maFramebuffers[0].xOrigin;
1178 y1 = (int32_t)maFramebuffers[0].yOrigin;
1179 x2 = mpDrv->IConnector.cx + (int32_t)maFramebuffers[0].xOrigin;
1180 y2 = mpDrv->IConnector.cy + (int32_t)maFramebuffers[0].yOrigin;
1181 }
1182 for (unsigned i = 1; i < mcMonitors; ++i)
1183 {
1184 if (!maFramebuffers[i].fDisabled)
1185 {
1186 x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
1187 y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
1188 x2 = RT_MAX(x2, maFramebuffers[i].xOrigin
1189 + (int32_t)maFramebuffers[i].w);
1190 y2 = RT_MAX(y2, maFramebuffers[i].yOrigin
1191 + (int32_t)maFramebuffers[i].h);
1192 }
1193 }
1194 *px1 = x1;
1195 *py1 = y1;
1196 *px2 = x2;
1197 *py2 = y2;
1198}
1199
1200static bool displayIntersectRect(RTRECT *prectResult,
1201 const RTRECT *prect1,
1202 const RTRECT *prect2)
1203{
1204 /* Initialize result to an empty record. */
1205 memset (prectResult, 0, sizeof (RTRECT));
1206
1207 int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
1208 int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
1209
1210 if (xLeftResult < xRightResult)
1211 {
1212 /* There is intersection by X. */
1213
1214 int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
1215 int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
1216
1217 if (yTopResult < yBottomResult)
1218 {
1219 /* There is intersection by Y. */
1220
1221 prectResult->xLeft = xLeftResult;
1222 prectResult->yTop = yTopResult;
1223 prectResult->xRight = xRightResult;
1224 prectResult->yBottom = yBottomResult;
1225
1226 return true;
1227 }
1228 }
1229
1230 return false;
1231}
1232
1233int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
1234{
1235 RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
1236 * sizeof (RTRECT));
1237 if (!pVisibleRegion)
1238 {
1239 return VERR_NO_TMP_MEMORY;
1240 }
1241
1242 unsigned uScreenId;
1243 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1244 {
1245 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1246
1247 if (!pFBInfo->pFramebuffer.isNull())
1248 {
1249 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
1250 {
1251 /* handle the case where new rectangles are received from the GA
1252 * when framebuffer resizing is in progress.
1253 * Just save the rectangles to be applied for later time when FB resizing is complete
1254 * (from handleResizeCompletedEMT).
1255 * This is done to prevent a race condition where a new rectangles are received
1256 * from the GA after a resize event and framebuffer resizing is still in progress
1257 * As a result the coordinates of the framebuffer are still
1258 * not updated and hence there is no intersection with the new rectangles passed
1259 * for the new region (THis is checked in the above if condition ). With 0 intersection,
1260 * cRectVisibleRegions = 0 is returned to the GUI and if GUI has invalidated its
1261 * earlier region then it draws nothihing and seamless mode doesn't display the
1262 * guest desktop.
1263 */
1264 SaveSeamlessRectLock();
1265 RTMemFree(pFBInfo->mpSavedVisibleRegion);
1266
1267 pFBInfo->mpSavedVisibleRegion = (RTRECT *)RTMemAlloc( RT_MAX(cRect, 1)
1268 * sizeof (RTRECT));
1269 if (pFBInfo->mpSavedVisibleRegion)
1270 {
1271 memcpy(pFBInfo->mpSavedVisibleRegion, pRect, cRect * sizeof(RTRECT));
1272 pFBInfo->mcSavedVisibleRegion = cRect;
1273 }
1274 else
1275 {
1276 pFBInfo->mcSavedVisibleRegion = 0;
1277 }
1278 SaveSeamlessRectUnLock();
1279 continue;
1280 }
1281 /* Prepare a new array of rectangles which intersect with the framebuffer.
1282 */
1283 RTRECT rectFramebuffer;
1284 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1285 {
1286 rectFramebuffer.xLeft = 0;
1287 rectFramebuffer.yTop = 0;
1288 if (mpDrv)
1289 {
1290 rectFramebuffer.xRight = mpDrv->IConnector.cx;
1291 rectFramebuffer.yBottom = mpDrv->IConnector.cy;
1292 }
1293 else
1294 {
1295 rectFramebuffer.xRight = 0;
1296 rectFramebuffer.yBottom = 0;
1297 }
1298 }
1299 else
1300 {
1301 rectFramebuffer.xLeft = pFBInfo->xOrigin;
1302 rectFramebuffer.yTop = pFBInfo->yOrigin;
1303 rectFramebuffer.xRight = pFBInfo->xOrigin + pFBInfo->w;
1304 rectFramebuffer.yBottom = pFBInfo->yOrigin + pFBInfo->h;
1305 }
1306
1307 uint32_t cRectVisibleRegion = 0;
1308
1309 uint32_t i;
1310 for (i = 0; i < cRect; i++)
1311 {
1312 if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
1313 {
1314 pVisibleRegion[cRectVisibleRegion].xLeft -= pFBInfo->xOrigin;
1315 pVisibleRegion[cRectVisibleRegion].yTop -= pFBInfo->yOrigin;
1316 pVisibleRegion[cRectVisibleRegion].xRight -= pFBInfo->xOrigin;
1317 pVisibleRegion[cRectVisibleRegion].yBottom -= pFBInfo->yOrigin;
1318
1319 cRectVisibleRegion++;
1320 }
1321 }
1322 pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
1323 }
1324 }
1325
1326#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1327 BOOL is3denabled = FALSE;
1328
1329 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
1330
1331 VMMDev *vmmDev = mParent->getVMMDev();
1332 if (is3denabled && vmmDev)
1333 {
1334 if (mhCrOglSvc)
1335 {
1336 RTRECT *pRectsCopy = (RTRECT *)RTMemAlloc( RT_MAX(cRect, 1)
1337 * sizeof (RTRECT));
1338 if (pRectsCopy)
1339 {
1340 memcpy(pRectsCopy, pRect, cRect * sizeof (RTRECT));
1341
1342 VBOXHGCMSVCPARM parm;
1343
1344 parm.type = VBOX_HGCM_SVC_PARM_PTR;
1345 parm.u.pointer.addr = pRectsCopy;
1346 parm.u.pointer.size = cRect * sizeof (RTRECT);
1347
1348 vmmDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SET_VISIBLE_REGION, &parm, displayCrAsyncCmdCompletion, this);
1349 }
1350 else
1351 AssertMsgFailed(("failed to allocate rects memory\n"));
1352 }
1353 else
1354 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
1355 }
1356#endif
1357
1358 RTMemTmpFree(pVisibleRegion);
1359
1360 return VINF_SUCCESS;
1361}
1362
1363int Display::handleQueryVisibleRegion(uint32_t *pcRect, PRTRECT pRect)
1364{
1365 // @todo Currently not used by the guest and is not implemented in framebuffers. Remove?
1366 return VERR_NOT_SUPPORTED;
1367}
1368
1369typedef struct _VBVADIRTYREGION
1370{
1371 /* Copies of object's pointers used by vbvaRgn functions. */
1372 DISPLAYFBINFO *paFramebuffers;
1373 unsigned cMonitors;
1374 Display *pDisplay;
1375 PPDMIDISPLAYPORT pPort;
1376
1377} VBVADIRTYREGION;
1378
1379static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors, Display *pd, PPDMIDISPLAYPORT pp)
1380{
1381 prgn->paFramebuffers = paFramebuffers;
1382 prgn->cMonitors = cMonitors;
1383 prgn->pDisplay = pd;
1384 prgn->pPort = pp;
1385
1386 unsigned uScreenId;
1387 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1388 {
1389 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1390
1391 RT_ZERO(pFBInfo->dirtyRect);
1392 }
1393}
1394
1395static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
1396{
1397 LogSunlover(("x = %d, y = %d, w = %d, h = %d\n",
1398 phdr->x, phdr->y, phdr->w, phdr->h));
1399
1400 /*
1401 * Here update rectangles are accumulated to form an update area.
1402 * @todo
1403 * Now the simplest method is used which builds one rectangle that
1404 * includes all update areas. A bit more advanced method can be
1405 * employed here. The method should be fast however.
1406 */
1407 if (phdr->w == 0 || phdr->h == 0)
1408 {
1409 /* Empty rectangle. */
1410 return;
1411 }
1412
1413 int32_t xRight = phdr->x + phdr->w;
1414 int32_t yBottom = phdr->y + phdr->h;
1415
1416 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1417
1418 if (pFBInfo->dirtyRect.xRight == 0)
1419 {
1420 /* This is the first rectangle to be added. */
1421 pFBInfo->dirtyRect.xLeft = phdr->x;
1422 pFBInfo->dirtyRect.yTop = phdr->y;
1423 pFBInfo->dirtyRect.xRight = xRight;
1424 pFBInfo->dirtyRect.yBottom = yBottom;
1425 }
1426 else
1427 {
1428 /* Adjust region coordinates. */
1429 if (pFBInfo->dirtyRect.xLeft > phdr->x)
1430 {
1431 pFBInfo->dirtyRect.xLeft = phdr->x;
1432 }
1433
1434 if (pFBInfo->dirtyRect.yTop > phdr->y)
1435 {
1436 pFBInfo->dirtyRect.yTop = phdr->y;
1437 }
1438
1439 if (pFBInfo->dirtyRect.xRight < xRight)
1440 {
1441 pFBInfo->dirtyRect.xRight = xRight;
1442 }
1443
1444 if (pFBInfo->dirtyRect.yBottom < yBottom)
1445 {
1446 pFBInfo->dirtyRect.yBottom = yBottom;
1447 }
1448 }
1449
1450 if (pFBInfo->fDefaultFormat)
1451 {
1452 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1453 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
1454 prgn->pDisplay->handleDisplayUpdateLegacy (phdr->x + pFBInfo->xOrigin,
1455 phdr->y + pFBInfo->yOrigin, phdr->w, phdr->h);
1456 }
1457
1458 return;
1459}
1460
1461static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn, unsigned uScreenId)
1462{
1463 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1464
1465 uint32_t w = pFBInfo->dirtyRect.xRight - pFBInfo->dirtyRect.xLeft;
1466 uint32_t h = pFBInfo->dirtyRect.yBottom - pFBInfo->dirtyRect.yTop;
1467
1468 if (!pFBInfo->fDefaultFormat && pFBInfo->pFramebuffer && w != 0 && h != 0)
1469 {
1470 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1471 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, pFBInfo->dirtyRect.xLeft, pFBInfo->dirtyRect.yTop, w, h);
1472 prgn->pDisplay->handleDisplayUpdateLegacy (pFBInfo->dirtyRect.xLeft + pFBInfo->xOrigin,
1473 pFBInfo->dirtyRect.yTop + pFBInfo->yOrigin, w, h);
1474 }
1475}
1476
1477static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
1478 bool fVideoAccelEnabled,
1479 bool fVideoAccelVRDP,
1480 uint32_t fu32SupportedOrders,
1481 DISPLAYFBINFO *paFBInfos,
1482 unsigned cFBInfos)
1483{
1484 if (pVbvaMemory)
1485 {
1486 /* This called only on changes in mode. So reset VRDP always. */
1487 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
1488
1489 if (fVideoAccelEnabled)
1490 {
1491 fu32Flags |= VBVA_F_MODE_ENABLED;
1492
1493 if (fVideoAccelVRDP)
1494 {
1495 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
1496
1497 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
1498 }
1499 }
1500
1501 pVbvaMemory->fu32ModeFlags = fu32Flags;
1502 }
1503
1504 unsigned uScreenId;
1505 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1506 {
1507 if (paFBInfos[uScreenId].pHostEvents)
1508 {
1509 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1510 }
1511 }
1512}
1513
1514#ifdef VBOX_WITH_HGSMI
1515static void vbvaSetMemoryFlagsHGSMI (unsigned uScreenId,
1516 uint32_t fu32SupportedOrders,
1517 bool fVideoAccelVRDP,
1518 DISPLAYFBINFO *pFBInfo)
1519{
1520 LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1521
1522 if (pFBInfo->pVBVAHostFlags)
1523 {
1524 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1525
1526 if (pFBInfo->fVBVAEnabled)
1527 {
1528 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1529
1530 if (fVideoAccelVRDP)
1531 {
1532 fu32HostEvents |= VBVA_F_MODE_VRDP;
1533 }
1534 }
1535
1536 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1537 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1538
1539 LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1540 }
1541}
1542
1543static void vbvaSetMemoryFlagsAllHGSMI (uint32_t fu32SupportedOrders,
1544 bool fVideoAccelVRDP,
1545 DISPLAYFBINFO *paFBInfos,
1546 unsigned cFBInfos)
1547{
1548 unsigned uScreenId;
1549
1550 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1551 {
1552 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1553 }
1554}
1555#endif /* VBOX_WITH_HGSMI */
1556
1557bool Display::VideoAccelAllowed (void)
1558{
1559 return true;
1560}
1561
1562int Display::vbvaLock(void)
1563{
1564 return RTCritSectEnter(&mVBVALock);
1565}
1566
1567void Display::vbvaUnlock(void)
1568{
1569 RTCritSectLeave(&mVBVALock);
1570}
1571
1572int Display::SaveSeamlessRectLock(void)
1573{
1574 return RTCritSectEnter(&mSaveSeamlessRectLock);
1575}
1576
1577void Display::SaveSeamlessRectUnLock(void)
1578{
1579 RTCritSectLeave(&mSaveSeamlessRectLock);
1580}
1581
1582
1583/**
1584 * @thread EMT
1585 */
1586int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1587{
1588 int rc;
1589 vbvaLock();
1590 rc = videoAccelEnable (fEnable, pVbvaMemory);
1591 vbvaUnlock();
1592 return rc;
1593}
1594
1595int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1596{
1597 int rc = VINF_SUCCESS;
1598
1599 /* Called each time the guest wants to use acceleration,
1600 * or when the VGA device disables acceleration,
1601 * or when restoring the saved state with accel enabled.
1602 *
1603 * VGA device disables acceleration on each video mode change
1604 * and on reset.
1605 *
1606 * Guest enabled acceleration at will. And it has to enable
1607 * acceleration after a mode change.
1608 */
1609 LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
1610 mfVideoAccelEnabled, fEnable, pVbvaMemory));
1611
1612 /* Strictly check parameters. Callers must not pass anything in the case. */
1613 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
1614
1615 if (!VideoAccelAllowed ())
1616 return VERR_NOT_SUPPORTED;
1617
1618 /*
1619 * Verify that the VM is in running state. If it is not,
1620 * then this must be postponed until it goes to running.
1621 */
1622 if (!mfMachineRunning)
1623 {
1624 Assert (!mfVideoAccelEnabled);
1625
1626 LogRelFlowFunc(("Machine is not yet running.\n"));
1627
1628 if (fEnable)
1629 {
1630 mfPendingVideoAccelEnable = fEnable;
1631 mpPendingVbvaMemory = pVbvaMemory;
1632 }
1633
1634 return rc;
1635 }
1636
1637 /* Check that current status is not being changed */
1638 if (mfVideoAccelEnabled == fEnable)
1639 return rc;
1640
1641 if (mfVideoAccelEnabled)
1642 {
1643 /* Process any pending orders and empty the VBVA ring buffer. */
1644 videoAccelFlush ();
1645 }
1646
1647 if (!fEnable && mpVbvaMemory)
1648 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
1649
1650 /* Safety precaution. There is no more VBVA until everything is setup! */
1651 mpVbvaMemory = NULL;
1652 mfVideoAccelEnabled = false;
1653
1654 /* Update entire display. */
1655 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].u32ResizeStatus == ResizeStatus_Void)
1656 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
1657
1658 /* Everything OK. VBVA status can be changed. */
1659
1660 /* Notify the VMMDev, which saves VBVA status in the saved state,
1661 * and needs to know current status.
1662 */
1663 VMMDev *pVMMDev = mParent->getVMMDev();
1664 if (pVMMDev)
1665 {
1666 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1667 if (pVMMDevPort)
1668 pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
1669 }
1670
1671 if (fEnable)
1672 {
1673 mpVbvaMemory = pVbvaMemory;
1674 mfVideoAccelEnabled = true;
1675
1676 /* Initialize the hardware memory. */
1677 vbvaSetMemoryFlags(mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1678 mpVbvaMemory->off32Data = 0;
1679 mpVbvaMemory->off32Free = 0;
1680
1681 memset(mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
1682 mpVbvaMemory->indexRecordFirst = 0;
1683 mpVbvaMemory->indexRecordFree = 0;
1684
1685 mfu32PendingVideoAccelDisable = false;
1686
1687 LogRel(("VBVA: Enabled.\n"));
1688 }
1689 else
1690 {
1691 LogRel(("VBVA: Disabled.\n"));
1692 }
1693
1694 LogRelFlowFunc(("VideoAccelEnable: rc = %Rrc.\n", rc));
1695
1696 return rc;
1697}
1698
1699/* Called always by one VRDP server thread. Can be thread-unsafe.
1700 */
1701void Display::VideoAccelVRDP (bool fEnable)
1702{
1703 LogRelFlowFunc(("fEnable = %d\n", fEnable));
1704
1705 vbvaLock();
1706
1707 int c = fEnable?
1708 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
1709 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
1710
1711 Assert (c >= 0);
1712
1713 if (c == 0)
1714 {
1715 /* The last client has disconnected, and the accel can be
1716 * disabled.
1717 */
1718 Assert (fEnable == false);
1719
1720 mfVideoAccelVRDP = false;
1721 mfu32SupportedOrders = 0;
1722
1723 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1724#ifdef VBOX_WITH_HGSMI
1725 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1726 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1727#endif /* VBOX_WITH_HGSMI */
1728
1729 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1730 }
1731 else if ( c == 1
1732 && !mfVideoAccelVRDP)
1733 {
1734 /* The first client has connected. Enable the accel.
1735 */
1736 Assert (fEnable == true);
1737
1738 mfVideoAccelVRDP = true;
1739 /* Supporting all orders. */
1740 mfu32SupportedOrders = ~0;
1741
1742 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1743#ifdef VBOX_WITH_HGSMI
1744 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1745 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1746#endif /* VBOX_WITH_HGSMI */
1747
1748 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1749 }
1750 else
1751 {
1752 /* A client is connected or disconnected but there is no change in the
1753 * accel state. It remains enabled.
1754 */
1755 Assert (mfVideoAccelVRDP == true);
1756 }
1757 vbvaUnlock();
1758}
1759
1760static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
1761{
1762 return true;
1763}
1764
1765static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
1766{
1767 if (cbDst >= VBVA_RING_BUFFER_SIZE)
1768 {
1769 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
1770 return;
1771 }
1772
1773 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
1774 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
1775 int32_t i32Diff = cbDst - u32BytesTillBoundary;
1776
1777 if (i32Diff <= 0)
1778 {
1779 /* Chunk will not cross buffer boundary. */
1780 memcpy (pu8Dst, src, cbDst);
1781 }
1782 else
1783 {
1784 /* Chunk crosses buffer boundary. */
1785 memcpy (pu8Dst, src, u32BytesTillBoundary);
1786 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
1787 }
1788
1789 /* Advance data offset. */
1790 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
1791
1792 return;
1793}
1794
1795
1796static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
1797{
1798 uint8_t *pu8New;
1799
1800 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
1801 *ppu8, *pcb, cbRecord));
1802
1803 if (*ppu8)
1804 {
1805 Assert (*pcb);
1806 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
1807 }
1808 else
1809 {
1810 Assert (!*pcb);
1811 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
1812 }
1813
1814 if (!pu8New)
1815 {
1816 /* Memory allocation failed, fail the function. */
1817 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
1818 cbRecord));
1819
1820 if (*ppu8)
1821 {
1822 RTMemFree (*ppu8);
1823 }
1824
1825 *ppu8 = NULL;
1826 *pcb = 0;
1827
1828 return false;
1829 }
1830
1831 /* Fetch data from the ring buffer. */
1832 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
1833
1834 *ppu8 = pu8New;
1835 *pcb = cbRecord;
1836
1837 return true;
1838}
1839
1840/* For contiguous chunks just return the address in the buffer.
1841 * For crossing boundary - allocate a buffer from heap.
1842 */
1843bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
1844{
1845 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
1846 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
1847
1848#ifdef DEBUG_sunlover
1849 LogFlowFunc(("first = %d, free = %d\n",
1850 indexRecordFirst, indexRecordFree));
1851#endif /* DEBUG_sunlover */
1852
1853 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
1854 {
1855 return false;
1856 }
1857
1858 if (indexRecordFirst == indexRecordFree)
1859 {
1860 /* No records to process. Return without assigning output variables. */
1861 return true;
1862 }
1863
1864 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
1865
1866#ifdef DEBUG_sunlover
1867 LogFlowFunc(("cbRecord = 0x%08X\n", pRecord->cbRecord));
1868#endif /* DEBUG_sunlover */
1869
1870 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
1871
1872 if (mcbVbvaPartial)
1873 {
1874 /* There is a partial read in process. Continue with it. */
1875
1876 Assert (mpu8VbvaPartial);
1877
1878 LogFlowFunc(("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
1879 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1880
1881 if (cbRecord > mcbVbvaPartial)
1882 {
1883 /* New data has been added to the record. */
1884 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1885 {
1886 return false;
1887 }
1888 }
1889
1890 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
1891 {
1892 /* The record is completed by guest. Return it to the caller. */
1893 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
1894 *pcbCmd = mcbVbvaPartial;
1895
1896 mpu8VbvaPartial = NULL;
1897 mcbVbvaPartial = 0;
1898
1899 /* Advance the record index. */
1900 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1901
1902#ifdef DEBUG_sunlover
1903 LogFlowFunc(("partial done ok, data = %d, free = %d\n",
1904 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1905#endif /* DEBUG_sunlover */
1906 }
1907
1908 return true;
1909 }
1910
1911 /* A new record need to be processed. */
1912 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
1913 {
1914 /* Current record is being written by guest. '=' is important here. */
1915 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
1916 {
1917 /* Partial read must be started. */
1918 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1919 {
1920 return false;
1921 }
1922
1923 LogFlowFunc(("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
1924 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1925 }
1926
1927 return true;
1928 }
1929
1930 /* Current record is complete. If it is not empty, process it. */
1931 if (cbRecord)
1932 {
1933 /* The size of largest contiguous chunk in the ring biffer. */
1934 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
1935
1936 /* The ring buffer pointer. */
1937 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
1938
1939 /* The pointer to data in the ring buffer. */
1940 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
1941
1942 /* Fetch or point the data. */
1943 if (u32BytesTillBoundary >= cbRecord)
1944 {
1945 /* The command does not cross buffer boundary. Return address in the buffer. */
1946 *ppHdr = (VBVACMDHDR *)src;
1947
1948 /* Advance data offset. */
1949 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1950 }
1951 else
1952 {
1953 /* The command crosses buffer boundary. Rare case, so not optimized. */
1954 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
1955
1956 if (!dst)
1957 {
1958 LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
1959 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1960 return false;
1961 }
1962
1963 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
1964
1965 *ppHdr = (VBVACMDHDR *)dst;
1966
1967#ifdef DEBUG_sunlover
1968 LogFlowFunc(("Allocated from heap %p\n", dst));
1969#endif /* DEBUG_sunlover */
1970 }
1971 }
1972
1973 *pcbCmd = cbRecord;
1974
1975 /* Advance the record index. */
1976 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1977
1978#ifdef DEBUG_sunlover
1979 LogFlowFunc(("done ok, data = %d, free = %d\n",
1980 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1981#endif /* DEBUG_sunlover */
1982
1983 return true;
1984}
1985
1986void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1987{
1988 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1989
1990 if ( (uint8_t *)pHdr >= au8RingBuffer
1991 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1992 {
1993 /* The pointer is inside ring buffer. Must be continuous chunk. */
1994 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1995
1996 /* Do nothing. */
1997
1998 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1999 }
2000 else
2001 {
2002 /* The pointer is outside. It is then an allocated copy. */
2003
2004#ifdef DEBUG_sunlover
2005 LogFlowFunc(("Free heap %p\n", pHdr));
2006#endif /* DEBUG_sunlover */
2007
2008 if ((uint8_t *)pHdr == mpu8VbvaPartial)
2009 {
2010 mpu8VbvaPartial = NULL;
2011 mcbVbvaPartial = 0;
2012 }
2013 else
2014 {
2015 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
2016 }
2017
2018 RTMemFree (pHdr);
2019 }
2020
2021 return;
2022}
2023
2024
2025/**
2026 * Called regularly on the DisplayRefresh timer.
2027 * Also on behalf of guest, when the ring buffer is full.
2028 *
2029 * @thread EMT
2030 */
2031void Display::VideoAccelFlush (void)
2032{
2033 vbvaLock();
2034 videoAccelFlush();
2035 vbvaUnlock();
2036}
2037
2038/* Under VBVA lock. DevVGA is not taken. */
2039void Display::videoAccelFlush (void)
2040{
2041#ifdef DEBUG_sunlover_2
2042 LogFlowFunc(("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
2043#endif /* DEBUG_sunlover_2 */
2044
2045 if (!mfVideoAccelEnabled)
2046 {
2047 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
2048 return;
2049 }
2050
2051 /* Here VBVA is enabled and we have the accelerator memory pointer. */
2052 Assert(mpVbvaMemory);
2053
2054#ifdef DEBUG_sunlover_2
2055 LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
2056 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2057#endif /* DEBUG_sunlover_2 */
2058
2059 /* Quick check for "nothing to update" case. */
2060 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
2061 {
2062 return;
2063 }
2064
2065 /* Process the ring buffer */
2066 unsigned uScreenId;
2067
2068 /* Initialize dirty rectangles accumulator. */
2069 VBVADIRTYREGION rgn;
2070 vbvaRgnInit (&rgn, maFramebuffers, mcMonitors, this, mpDrv->pUpPort);
2071
2072 for (;;)
2073 {
2074 VBVACMDHDR *phdr = NULL;
2075 uint32_t cbCmd = ~0;
2076
2077 /* Fetch the command data. */
2078 if (!vbvaFetchCmd (&phdr, &cbCmd))
2079 {
2080 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
2081 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2082
2083 /* Disable VBVA on those processing errors. */
2084 videoAccelEnable (false, NULL);
2085
2086 break;
2087 }
2088
2089 if (cbCmd == uint32_t(~0))
2090 {
2091 /* No more commands yet in the queue. */
2092 break;
2093 }
2094
2095 if (cbCmd != 0)
2096 {
2097#ifdef DEBUG_sunlover
2098 LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
2099 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
2100#endif /* DEBUG_sunlover */
2101
2102 VBVACMDHDR hdrSaved = *phdr;
2103
2104 int x = phdr->x;
2105 int y = phdr->y;
2106 int w = phdr->w;
2107 int h = phdr->h;
2108
2109 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
2110
2111 phdr->x = (int16_t)x;
2112 phdr->y = (int16_t)y;
2113 phdr->w = (uint16_t)w;
2114 phdr->h = (uint16_t)h;
2115
2116 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
2117
2118 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2119 {
2120 /* Handle the command.
2121 *
2122 * Guest is responsible for updating the guest video memory.
2123 * The Windows guest does all drawing using Eng*.
2124 *
2125 * For local output, only dirty rectangle information is used
2126 * to update changed areas.
2127 *
2128 * Dirty rectangles are accumulated to exclude overlapping updates and
2129 * group small updates to a larger one.
2130 */
2131
2132 /* Accumulate the update. */
2133 vbvaRgnDirtyRect (&rgn, uScreenId, phdr);
2134
2135 /* Forward the command to VRDP server. */
2136 mParent->consoleVRDPServer()->SendUpdate (uScreenId, phdr, cbCmd);
2137
2138 *phdr = hdrSaved;
2139 }
2140 }
2141
2142 vbvaReleaseCmd (phdr, cbCmd);
2143 }
2144
2145 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
2146 {
2147 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
2148 {
2149 /* Draw the framebuffer. */
2150 vbvaRgnUpdateFramebuffer (&rgn, uScreenId);
2151 }
2152 }
2153}
2154
2155int Display::videoAccelRefreshProcess(void)
2156{
2157 int rc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
2158
2159 vbvaLock();
2160
2161 if (ASMAtomicCmpXchgU32(&mfu32PendingVideoAccelDisable, false, true))
2162 {
2163 videoAccelEnable (false, NULL);
2164 }
2165 else if (mfPendingVideoAccelEnable)
2166 {
2167 /* Acceleration was enabled while machine was not yet running
2168 * due to restoring from saved state. Update entire display and
2169 * actually enable acceleration.
2170 */
2171 Assert(mpPendingVbvaMemory);
2172
2173 /* Acceleration can not be yet enabled.*/
2174 Assert(mpVbvaMemory == NULL);
2175 Assert(!mfVideoAccelEnabled);
2176
2177 if (mfMachineRunning)
2178 {
2179 videoAccelEnable (mfPendingVideoAccelEnable,
2180 mpPendingVbvaMemory);
2181
2182 /* Reset the pending state. */
2183 mfPendingVideoAccelEnable = false;
2184 mpPendingVbvaMemory = NULL;
2185 }
2186
2187 rc = VINF_TRY_AGAIN;
2188 }
2189 else
2190 {
2191 Assert(mpPendingVbvaMemory == NULL);
2192
2193 if (mfVideoAccelEnabled)
2194 {
2195 Assert(mpVbvaMemory);
2196 videoAccelFlush ();
2197
2198 rc = VINF_SUCCESS; /* VBVA processed, no need to a display update. */
2199 }
2200 }
2201
2202 vbvaUnlock();
2203
2204 return rc;
2205}
2206
2207
2208// IDisplay methods
2209/////////////////////////////////////////////////////////////////////////////
2210STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
2211 ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
2212 LONG *aXOrigin, LONG *aYOrigin)
2213{
2214 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2215
2216 AutoCaller autoCaller(this);
2217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2218
2219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2220
2221 uint32_t u32Width = 0;
2222 uint32_t u32Height = 0;
2223 uint32_t u32BitsPerPixel = 0;
2224 int32_t xOrigin = 0;
2225 int32_t yOrigin = 0;
2226
2227 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2228 {
2229 CHECK_CONSOLE_DRV(mpDrv);
2230
2231 u32Width = mpDrv->IConnector.cx;
2232 u32Height = mpDrv->IConnector.cy;
2233 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &u32BitsPerPixel);
2234 AssertRC(rc);
2235 }
2236 else if (aScreenId < mcMonitors)
2237 {
2238 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2239 u32Width = pFBInfo->w;
2240 u32Height = pFBInfo->h;
2241 u32BitsPerPixel = pFBInfo->u16BitsPerPixel;
2242 xOrigin = pFBInfo->xOrigin;
2243 yOrigin = pFBInfo->yOrigin;
2244 }
2245 else
2246 {
2247 return E_INVALIDARG;
2248 }
2249
2250 if (aWidth)
2251 *aWidth = u32Width;
2252 if (aHeight)
2253 *aHeight = u32Height;
2254 if (aBitsPerPixel)
2255 *aBitsPerPixel = u32BitsPerPixel;
2256 if (aXOrigin)
2257 *aXOrigin = xOrigin;
2258 if (aYOrigin)
2259 *aYOrigin = yOrigin;
2260
2261 return S_OK;
2262}
2263
2264STDMETHODIMP Display::SetFramebuffer(ULONG aScreenId, IFramebuffer *aFramebuffer)
2265{
2266 LogRelFlowFunc(("\n"));
2267
2268 if (aFramebuffer != NULL)
2269 CheckComArgOutPointerValid(aFramebuffer);
2270
2271 AutoCaller autoCaller(this);
2272 if (FAILED(autoCaller.rc()))
2273 return autoCaller.rc();
2274
2275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2276
2277 Console::SafeVMPtrQuiet ptrVM(mParent);
2278 if (ptrVM.isOk())
2279 {
2280 /* Must release the lock here because the changeFramebuffer will
2281 * also obtain it. */
2282 alock.release();
2283
2284 /* send request to the EMT thread */
2285 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
2286 (PFNRT)changeFramebuffer, 3, this, aFramebuffer, aScreenId);
2287
2288 alock.acquire();
2289
2290 ComAssertRCRet (vrc, E_FAIL);
2291
2292#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2293 {
2294 BOOL is3denabled;
2295 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2296
2297 if (is3denabled)
2298 {
2299 VBOXHGCMSVCPARM parm;
2300
2301 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
2302 parm.u.uint32 = aScreenId;
2303
2304 VMMDev *pVMMDev = mParent->getVMMDev();
2305
2306 alock.release();
2307
2308 if (pVMMDev)
2309 {
2310#if 0
2311 if (mhCrOglSvc)
2312 pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SCREEN_CHANGED, &parm, NULL, NULL);
2313 else
2314 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
2315#else
2316 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
2317#endif
2318 }
2319 /*ComAssertRCRet (vrc, E_FAIL);*/
2320
2321 alock.acquire();
2322 }
2323 }
2324#endif /* VBOX_WITH_CROGL */
2325 }
2326 else
2327 {
2328 /* No VM is created (VM is powered off), do a direct call */
2329 int vrc = changeFramebuffer (this, aFramebuffer, aScreenId);
2330 ComAssertRCRet (vrc, E_FAIL);
2331 }
2332
2333 return S_OK;
2334}
2335
2336STDMETHODIMP Display::GetFramebuffer(ULONG aScreenId,
2337 IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
2338{
2339 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2340
2341 CheckComArgOutPointerValid(aFramebuffer);
2342
2343 AutoCaller autoCaller(this);
2344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2345
2346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2347
2348 if (aScreenId != 0 && aScreenId >= mcMonitors)
2349 return E_INVALIDARG;
2350
2351 /* @todo this should be actually done on EMT. */
2352 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2353
2354 *aFramebuffer = pFBInfo->pFramebuffer;
2355 if (*aFramebuffer)
2356 (*aFramebuffer)->AddRef ();
2357 if (aXOrigin)
2358 *aXOrigin = pFBInfo->xOrigin;
2359 if (aYOrigin)
2360 *aYOrigin = pFBInfo->yOrigin;
2361
2362 return S_OK;
2363}
2364
2365STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
2366 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
2367 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
2368{
2369 AutoCaller autoCaller(this);
2370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2371
2372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 CHECK_CONSOLE_DRV(mpDrv);
2375
2376 /*
2377 * Do some rough checks for valid input
2378 */
2379 ULONG width = aWidth;
2380 if (!width)
2381 width = mpDrv->IConnector.cx;
2382 ULONG height = aHeight;
2383 if (!height)
2384 height = mpDrv->IConnector.cy;
2385 ULONG bpp = aBitsPerPixel;
2386 if (!bpp)
2387 {
2388 uint32_t cBits = 0;
2389 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
2390 AssertRC(rc);
2391 bpp = cBits;
2392 }
2393 ULONG cMonitors;
2394 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
2395 if (cMonitors == 0 && aDisplay > 0)
2396 return E_INVALIDARG;
2397 if (aDisplay >= cMonitors)
2398 return E_INVALIDARG;
2399
2400 /*
2401 * sunlover 20070614: It is up to the guest to decide whether the hint is
2402 * valid. Therefore don't do any VRAM sanity checks here!
2403 */
2404
2405 /* Have to release the lock because the pfnRequestDisplayChange
2406 * will call EMT. */
2407 alock.release();
2408
2409 VMMDev *pVMMDev = mParent->getVMMDev();
2410 if (pVMMDev)
2411 {
2412 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2413 if (pVMMDevPort)
2414 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
2415 aDisplay, aOriginX, aOriginY,
2416 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
2417 }
2418 return S_OK;
2419}
2420
2421STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
2422{
2423 AutoCaller autoCaller(this);
2424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2425
2426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
2429 alock.release();
2430
2431 VMMDev *pVMMDev = mParent->getVMMDev();
2432 if (pVMMDev)
2433 {
2434 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2435 if (pVMMDevPort)
2436 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
2437 }
2438
2439#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2440 if (!enabled)
2441 {
2442 BOOL is3denabled = FALSE;
2443
2444 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2445
2446 VMMDev *vmmDev = mParent->getVMMDev();
2447 if (is3denabled && vmmDev)
2448 {
2449 VBOXHGCMSVCPARM parm;
2450
2451 parm.type = VBOX_HGCM_SVC_PARM_PTR;
2452 /* NULL means disable */
2453 parm.u.pointer.addr = NULL;
2454 parm.u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
2455
2456 if (mhCrOglSvc)
2457 vmmDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SET_VISIBLE_REGION, &parm, NULL, NULL);
2458 else
2459 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
2460
2461 }
2462 }
2463#endif
2464 return S_OK;
2465}
2466
2467#ifdef VBOX_WITH_CROGL
2468BOOL Display::displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pu8Data, uint32_t u32Width, uint32_t u32Height)
2469{
2470 BOOL is3denabled;
2471 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2472 if (is3denabled && pDisplay->mCrOglCallbacks.pfnHasData())
2473 {
2474 VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
2475 if (pVMMDev)
2476 {
2477 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)RTMemAlloc(sizeof (*pScreenshot));
2478 if (pScreenshot)
2479 {
2480 /* screen id or CRSCREEN_ALL to specify all enabled */
2481 pScreenshot->u32Screen = aScreenId;
2482 pScreenshot->u32Width = u32Width;
2483 pScreenshot->u32Height = u32Height;
2484 pScreenshot->u32Pitch = u32Width * 4;
2485 pScreenshot->pvBuffer = pu8Data;
2486 pScreenshot->pvContext = NULL;
2487 pScreenshot->pfnScreenshotBegin = NULL;
2488 pScreenshot->pfnScreenshotPerform = NULL;
2489 pScreenshot->pfnScreenshotEnd = NULL;
2490
2491 VBOXHGCMSVCPARM parm;
2492
2493 parm.type = VBOX_HGCM_SVC_PARM_PTR;
2494 parm.u.pointer.addr = pScreenshot;
2495 parm.u.pointer.size = sizeof (*pScreenshot);
2496
2497 int rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_TAKE_SCREENSHOT, 1, &parm);
2498
2499 RTMemFree(pScreenshot);
2500
2501 if (RT_SUCCESS(rc))
2502 return TRUE;
2503 else
2504 {
2505 AssertMsgFailed(("failed to get screenshot data from crOgl %d\n", rc));
2506 /* fall back to the non-3d mechanism */
2507 }
2508 }
2509 }
2510 }
2511 return FALSE;
2512}
2513#endif
2514
2515int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
2516{
2517 int rc;
2518 pDisplay->vbvaLock();
2519 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2520 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2521 {
2522 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
2523 }
2524 else if (aScreenId < pDisplay->mcMonitors)
2525 {
2526 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2527
2528 uint32_t width = pFBInfo->w;
2529 uint32_t height = pFBInfo->h;
2530
2531 /* Allocate 32 bit per pixel bitmap. */
2532 size_t cbRequired = width * 4 * height;
2533
2534 if (cbRequired)
2535 {
2536 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
2537
2538 if (pu8Data == NULL)
2539 {
2540 rc = VERR_NO_MEMORY;
2541 }
2542 else
2543 {
2544 /* Copy guest VRAM to the allocated 32bpp buffer. */
2545 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2546 int32_t xSrc = 0;
2547 int32_t ySrc = 0;
2548 uint32_t u32SrcWidth = width;
2549 uint32_t u32SrcHeight = height;
2550 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2551 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2552
2553 uint8_t *pu8Dst = pu8Data;
2554 int32_t xDst = 0;
2555 int32_t yDst = 0;
2556 uint32_t u32DstWidth = u32SrcWidth;
2557 uint32_t u32DstHeight = u32SrcHeight;
2558 uint32_t u32DstLineSize = u32DstWidth * 4;
2559 uint32_t u32DstBitsPerPixel = 32;
2560
2561 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2562 width, height,
2563 pu8Src,
2564 xSrc, ySrc,
2565 u32SrcWidth, u32SrcHeight,
2566 u32SrcLineSize, u32SrcBitsPerPixel,
2567 pu8Dst,
2568 xDst, yDst,
2569 u32DstWidth, u32DstHeight,
2570 u32DstLineSize, u32DstBitsPerPixel);
2571 if (RT_SUCCESS(rc))
2572 {
2573 *ppu8Data = pu8Data;
2574 *pcbData = cbRequired;
2575 *pu32Width = width;
2576 *pu32Height = height;
2577 }
2578 else
2579 {
2580 RTMemFree(pu8Data);
2581 }
2582 }
2583 }
2584 else
2585 {
2586 /* No image. */
2587 *ppu8Data = NULL;
2588 *pcbData = 0;
2589 *pu32Width = 0;
2590 *pu32Height = 0;
2591 rc = VINF_SUCCESS;
2592 }
2593 }
2594 else
2595 {
2596 rc = VERR_INVALID_PARAMETER;
2597 }
2598 pDisplay->vbvaUnlock();
2599 return rc;
2600}
2601
2602static int displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2603 BYTE *address, ULONG width, ULONG height)
2604{
2605 uint8_t *pu8Data = NULL;
2606 size_t cbData = 0;
2607 uint32_t cx = 0;
2608 uint32_t cy = 0;
2609 int vrc = VINF_SUCCESS;
2610
2611# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2612 if (Display::displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t*)address, width, height))
2613 return VINF_SUCCESS;
2614#endif
2615
2616 int cRetries = 5;
2617
2618 while (cRetries-- > 0)
2619 {
2620 /* Note! Not sure if the priority call is such a good idea here, but
2621 it would be nice to have an accurate screenshot for the bug
2622 report if the VM deadlocks. */
2623 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
2624 pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
2625 if (vrc != VERR_TRY_AGAIN)
2626 {
2627 break;
2628 }
2629
2630 RTThreadSleep(10);
2631 }
2632
2633 if (RT_SUCCESS(vrc) && pu8Data)
2634 {
2635 if (cx == width && cy == height)
2636 {
2637 /* No scaling required. */
2638 memcpy(address, pu8Data, cbData);
2639 }
2640 else
2641 {
2642 /* Scale. */
2643 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2644
2645 uint8_t *dst = address;
2646 uint8_t *src = pu8Data;
2647 int dstW = width;
2648 int dstH = height;
2649 int srcW = cx;
2650 int srcH = cy;
2651 int iDeltaLine = cx * 4;
2652
2653 BitmapScale32(dst,
2654 dstW, dstH,
2655 src,
2656 iDeltaLine,
2657 srcW, srcH);
2658 }
2659
2660 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2661 {
2662 /* This can be called from any thread. */
2663 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pu8Data);
2664 }
2665 else
2666 {
2667 RTMemFree(pu8Data);
2668 }
2669 }
2670
2671 return vrc;
2672}
2673
2674STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
2675{
2676 /// @todo (r=dmik) this function may take too long to complete if the VM
2677 // is doing something like saving state right now. Which, in case if it
2678 // is called on the GUI thread, will make it unresponsive. We should
2679 // check the machine state here (by enclosing the check and VMRequCall
2680 // within the Console lock to make it atomic).
2681
2682 LogRelFlowFunc(("address=%p, width=%d, height=%d\n",
2683 address, width, height));
2684
2685 CheckComArgNotNull(address);
2686 CheckComArgExpr(width, width != 0);
2687 CheckComArgExpr(height, height != 0);
2688
2689 /* Do not allow too large screenshots. This also filters out negative
2690 * values passed as either 'width' or 'height'.
2691 */
2692 CheckComArgExpr(width, width <= 32767);
2693 CheckComArgExpr(height, height <= 32767);
2694
2695 AutoCaller autoCaller(this);
2696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2697
2698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 if (!mpDrv)
2701 return E_FAIL;
2702
2703 Console::SafeVMPtr ptrVM(mParent);
2704 if (!ptrVM.isOk())
2705 return ptrVM.rc();
2706
2707 HRESULT rc = S_OK;
2708
2709 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2710
2711 /* Release lock because other thread (EMT) is called and it may initiate a resize
2712 * which also needs lock.
2713 *
2714 * This method does not need the lock anymore.
2715 */
2716 alock.release();
2717
2718 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, address, width, height);
2719
2720 if (vrc == VERR_NOT_IMPLEMENTED)
2721 rc = setError(E_NOTIMPL,
2722 tr("This feature is not implemented"));
2723 else if (vrc == VERR_TRY_AGAIN)
2724 rc = setError(E_UNEXPECTED,
2725 tr("This feature is not available at this time"));
2726 else if (RT_FAILURE(vrc))
2727 rc = setError(VBOX_E_IPRT_ERROR,
2728 tr("Could not take a screenshot (%Rrc)"), vrc);
2729
2730 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2731 return rc;
2732}
2733
2734STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG height,
2735 ComSafeArrayOut(BYTE, aScreenData))
2736{
2737 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
2738
2739 CheckComArgOutSafeArrayPointerValid(aScreenData);
2740 CheckComArgExpr(width, width != 0);
2741 CheckComArgExpr(height, height != 0);
2742
2743 /* Do not allow too large screenshots. This also filters out negative
2744 * values passed as either 'width' or 'height'.
2745 */
2746 CheckComArgExpr(width, width <= 32767);
2747 CheckComArgExpr(height, height <= 32767);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 if (!mpDrv)
2755 return E_FAIL;
2756
2757 Console::SafeVMPtr ptrVM(mParent);
2758 if (!ptrVM.isOk())
2759 return ptrVM.rc();
2760
2761 HRESULT rc = S_OK;
2762
2763 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2764
2765 /* Release lock because other thread (EMT) is called and it may initiate a resize
2766 * which also needs lock.
2767 *
2768 * This method does not need the lock anymore.
2769 */
2770 alock.release();
2771
2772 size_t cbData = width * 4 * height;
2773 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
2774
2775 if (!pu8Data)
2776 return E_OUTOFMEMORY;
2777
2778 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
2779
2780 if (RT_SUCCESS(vrc))
2781 {
2782 /* Convert pixels to format expected by the API caller: [0] R, [1] G, [2] B, [3] A. */
2783 uint8_t *pu8 = pu8Data;
2784 unsigned cPixels = width * height;
2785 while (cPixels)
2786 {
2787 uint8_t u8 = pu8[0];
2788 pu8[0] = pu8[2];
2789 pu8[2] = u8;
2790 pu8[3] = 0xff;
2791 cPixels--;
2792 pu8 += 4;
2793 }
2794
2795 com::SafeArray<BYTE> screenData(cbData);
2796 screenData.initFrom(pu8Data, cbData);
2797 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
2798 }
2799 else if (vrc == VERR_NOT_IMPLEMENTED)
2800 rc = setError(E_NOTIMPL,
2801 tr("This feature is not implemented"));
2802 else
2803 rc = setError(VBOX_E_IPRT_ERROR,
2804 tr("Could not take a screenshot (%Rrc)"), vrc);
2805
2806 RTMemFree(pu8Data);
2807
2808 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2809 return rc;
2810}
2811
2812STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULONG height,
2813 ComSafeArrayOut(BYTE, aScreenData))
2814{
2815 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
2816
2817 CheckComArgOutSafeArrayPointerValid(aScreenData);
2818 CheckComArgExpr(width, width != 0);
2819 CheckComArgExpr(height, height != 0);
2820
2821 /* Do not allow too large screenshots. This also filters out negative
2822 * values passed as either 'width' or 'height'.
2823 */
2824 CheckComArgExpr(width, width <= 32767);
2825 CheckComArgExpr(height, height <= 32767);
2826
2827 AutoCaller autoCaller(this);
2828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2829
2830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 CHECK_CONSOLE_DRV(mpDrv);
2833
2834 Console::SafeVMPtr ptrVM(mParent);
2835 if (!ptrVM.isOk())
2836 return ptrVM.rc();
2837
2838 HRESULT rc = S_OK;
2839
2840 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2841
2842 /* Release lock because other thread (EMT) is called and it may initiate a resize
2843 * which also needs lock.
2844 *
2845 * This method does not need the lock anymore.
2846 */
2847 alock.release();
2848
2849 size_t cbData = width * 4 * height;
2850 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
2851
2852 if (!pu8Data)
2853 return E_OUTOFMEMORY;
2854
2855 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
2856
2857 if (RT_SUCCESS(vrc))
2858 {
2859 uint8_t *pu8PNG = NULL;
2860 uint32_t cbPNG = 0;
2861 uint32_t cxPNG = 0;
2862 uint32_t cyPNG = 0;
2863
2864 vrc = DisplayMakePNG(pu8Data, width, height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2865 if (RT_SUCCESS(vrc))
2866 {
2867 com::SafeArray<BYTE> screenData(cbPNG);
2868 screenData.initFrom(pu8PNG, cbPNG);
2869 if (pu8PNG)
2870 RTMemFree(pu8PNG);
2871
2872 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
2873 }
2874 else
2875 {
2876 if (pu8PNG)
2877 RTMemFree(pu8PNG);
2878 rc = setError(VBOX_E_IPRT_ERROR,
2879 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2880 }
2881 }
2882 else if (vrc == VERR_NOT_IMPLEMENTED)
2883 rc = setError(E_NOTIMPL,
2884 tr("This feature is not implemented"));
2885 else
2886 rc = setError(VBOX_E_IPRT_ERROR,
2887 tr("Could not take a screenshot (%Rrc)"), vrc);
2888
2889 RTMemFree(pu8Data);
2890
2891 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2892 return rc;
2893}
2894
2895int Display::VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
2896{
2897#ifdef VBOX_WITH_VPX
2898 com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
2899 for (unsigned i = 0; i < Screens.size(); i++)
2900 maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
2901 return VINF_SUCCESS;
2902#else
2903 return VERR_NOT_IMPLEMENTED;
2904#endif
2905}
2906
2907/**
2908 * Start video capturing. Does nothing if capturing is already active.
2909 */
2910int Display::VideoCaptureStart()
2911{
2912#ifdef VBOX_WITH_VPX
2913 if (VideoRecIsEnabled(mpVideoRecCtx))
2914 return VINF_SUCCESS;
2915
2916 int rc = VideoRecContextCreate(&mpVideoRecCtx, mcMonitors);
2917 if (RT_FAILURE(rc))
2918 {
2919 LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
2920 return rc;
2921 }
2922 ComPtr<IMachine> pMachine = mParent->machine();
2923 com::SafeArray<BOOL> screens;
2924 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
2925 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2926 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
2927 maVideoRecEnabled[i] = i < screens.size() && screens[i];
2928 ULONG ulWidth;
2929 hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
2930 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2931 ULONG ulHeight;
2932 hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
2933 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2934 ULONG ulRate;
2935 hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
2936 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2937 ULONG ulFPS;
2938 hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
2939 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2940 BSTR strFile;
2941 hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
2942 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2943 RTTIMESPEC ts;
2944 RTTimeNow(&ts);
2945 RTTIME time;
2946 RTTimeExplode(&time, &ts);
2947 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
2948 {
2949 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
2950 char *pszSuff = RTPathSuffix(pszAbsPath);
2951 if (pszSuff)
2952 pszSuff = RTStrDup(pszSuff);
2953 RTPathStripSuffix(pszAbsPath);
2954 if (!pszAbsPath)
2955 rc = VERR_INVALID_PARAMETER;
2956 if (!pszSuff)
2957 pszSuff = RTStrDup(".webm");
2958 char *pszName = NULL;
2959 if (RT_SUCCESS(rc))
2960 {
2961 if (mcMonitors > 1)
2962 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff);
2963 else
2964 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff);
2965 }
2966 if (RT_SUCCESS(rc))
2967 {
2968 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
2969 pszName, ulWidth, ulHeight, ulRate, ulFPS);
2970 if (rc == VERR_ALREADY_EXISTS)
2971 {
2972 RTStrFree(pszName);
2973 pszName = NULL;
2974
2975 if (mcMonitors > 1)
2976 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
2977 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
2978 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
2979 uScreen+1, pszSuff);
2980 else
2981 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
2982 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
2983 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
2984 pszSuff);
2985 if (RT_SUCCESS(rc))
2986 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
2987 pszName, ulWidth, ulHeight, ulRate, ulFPS);
2988 }
2989 }
2990
2991 if (RT_SUCCESS(rc))
2992 LogRel(("WebM/VP8 video recording screen #%u with %ux%u @ %u kbps, %u fps to '%s' enabled.\n",
2993 uScreen, ulWidth, ulHeight, ulRate, ulFPS, pszName));
2994 else
2995 LogRel(("Failed to initialize video recording context #%u (%Rrc)!\n", uScreen, rc));
2996 RTStrFree(pszName);
2997 RTStrFree(pszSuff);
2998 RTStrFree(pszAbsPath);
2999 }
3000 return rc;
3001#else
3002 return VERR_NOT_IMPLEMENTED;
3003#endif
3004}
3005
3006/**
3007 * Stop video capturing. Does nothing if video capturing is not active.
3008 */
3009void Display::VideoCaptureStop()
3010{
3011#ifdef VBOX_WITH_VPX
3012 if (VideoRecIsEnabled(mpVideoRecCtx))
3013 LogRel(("WebM/VP8 video recording stopped.\n"));
3014 VideoRecContextClose(mpVideoRecCtx);
3015 mpVideoRecCtx = NULL;
3016#endif
3017}
3018
3019int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
3020 ULONG x, ULONG y, ULONG width, ULONG height)
3021{
3022 int rc = VINF_SUCCESS;
3023 pDisplay->vbvaLock();
3024
3025 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
3026
3027 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3028 {
3029 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3030 {
3031 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
3032 }
3033 }
3034 else if (aScreenId < pDisplay->mcMonitors)
3035 {
3036 /* Copy the bitmap to the guest VRAM. */
3037 const uint8_t *pu8Src = address;
3038 int32_t xSrc = 0;
3039 int32_t ySrc = 0;
3040 uint32_t u32SrcWidth = width;
3041 uint32_t u32SrcHeight = height;
3042 uint32_t u32SrcLineSize = width * 4;
3043 uint32_t u32SrcBitsPerPixel = 32;
3044
3045 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
3046 int32_t xDst = x;
3047 int32_t yDst = y;
3048 uint32_t u32DstWidth = pFBInfo->w;
3049 uint32_t u32DstHeight = pFBInfo->h;
3050 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
3051 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
3052
3053 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3054 width, height,
3055 pu8Src,
3056 xSrc, ySrc,
3057 u32SrcWidth, u32SrcHeight,
3058 u32SrcLineSize, u32SrcBitsPerPixel,
3059 pu8Dst,
3060 xDst, yDst,
3061 u32DstWidth, u32DstHeight,
3062 u32DstLineSize, u32DstBitsPerPixel);
3063 if (RT_SUCCESS(rc))
3064 {
3065 if (!pFBInfo->pFramebuffer.isNull())
3066 {
3067 /* Update the changed screen area. When framebuffer uses VRAM directly, just notify
3068 * it to update. And for default format, render the guest VRAM to framebuffer.
3069 */
3070 if ( pFBInfo->fDefaultFormat
3071 && !pFBInfo->fDisabled)
3072 {
3073 address = NULL;
3074 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
3075 if (SUCCEEDED(hrc) && address != NULL)
3076 {
3077 pu8Src = pFBInfo->pu8FramebufferVRAM;
3078 xSrc = x;
3079 ySrc = y;
3080 u32SrcWidth = pFBInfo->w;
3081 u32SrcHeight = pFBInfo->h;
3082 u32SrcLineSize = pFBInfo->u32LineSize;
3083 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3084
3085 /* Default format is 32 bpp. */
3086 pu8Dst = address;
3087 xDst = xSrc;
3088 yDst = ySrc;
3089 u32DstWidth = u32SrcWidth;
3090 u32DstHeight = u32SrcHeight;
3091 u32DstLineSize = u32DstWidth * 4;
3092 u32DstBitsPerPixel = 32;
3093
3094 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3095 width, height,
3096 pu8Src,
3097 xSrc, ySrc,
3098 u32SrcWidth, u32SrcHeight,
3099 u32SrcLineSize, u32SrcBitsPerPixel,
3100 pu8Dst,
3101 xDst, yDst,
3102 u32DstWidth, u32DstHeight,
3103 u32DstLineSize, u32DstBitsPerPixel);
3104 }
3105 }
3106
3107 pDisplay->handleDisplayUpdate(aScreenId, x, y, width, height);
3108 }
3109 }
3110 }
3111 else
3112 {
3113 rc = VERR_INVALID_PARAMETER;
3114 }
3115
3116 if ( RT_SUCCESS(rc)
3117 && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
3118 pDisplay->mParent->consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
3119
3120 pDisplay->vbvaUnlock();
3121 return rc;
3122}
3123
3124STDMETHODIMP Display::DrawToScreen(ULONG aScreenId, BYTE *address,
3125 ULONG x, ULONG y, ULONG width, ULONG height)
3126{
3127 /// @todo (r=dmik) this function may take too long to complete if the VM
3128 // is doing something like saving state right now. Which, in case if it
3129 // is called on the GUI thread, will make it unresponsive. We should
3130 // check the machine state here (by enclosing the check and VMRequCall
3131 // within the Console lock to make it atomic).
3132
3133 LogRelFlowFunc(("address=%p, x=%d, y=%d, width=%d, height=%d\n",
3134 (void *)address, x, y, width, height));
3135
3136 CheckComArgNotNull(address);
3137 CheckComArgExpr(width, width != 0);
3138 CheckComArgExpr(height, height != 0);
3139
3140 AutoCaller autoCaller(this);
3141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3142
3143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 CHECK_CONSOLE_DRV(mpDrv);
3146
3147 Console::SafeVMPtr ptrVM(mParent);
3148 if (!ptrVM.isOk())
3149 return ptrVM.rc();
3150
3151 /* Release lock because the call scheduled on EMT may also try to take it. */
3152 alock.release();
3153
3154 /*
3155 * Again we're lazy and make the graphics device do all the
3156 * dirty conversion work.
3157 */
3158 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
3159 this, aScreenId, address, x, y, width, height);
3160
3161 /*
3162 * If the function returns not supported, we'll have to do all the
3163 * work ourselves using the framebuffer.
3164 */
3165 HRESULT rc = S_OK;
3166 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
3167 {
3168 /** @todo implement generic fallback for screen blitting. */
3169 rc = E_NOTIMPL;
3170 }
3171 else if (RT_FAILURE(rcVBox))
3172 rc = setError(VBOX_E_IPRT_ERROR,
3173 tr("Could not draw to the screen (%Rrc)"), rcVBox);
3174//@todo
3175// else
3176// {
3177// /* All ok. Redraw the screen. */
3178// handleDisplayUpdate (x, y, width, height);
3179// }
3180
3181 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3182 return rc;
3183}
3184
3185void Display::InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
3186{
3187 pDisplay->vbvaLock();
3188 unsigned uScreenId;
3189 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
3190 {
3191 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3192
3193 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
3194 {
3195 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort);
3196 }
3197 else
3198 {
3199 if ( !pFBInfo->pFramebuffer.isNull()
3200 && !pFBInfo->fDisabled
3201 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3202 {
3203 /* Render complete VRAM screen to the framebuffer.
3204 * When framebuffer uses VRAM directly, just notify it to update.
3205 */
3206 if (pFBInfo->fDefaultFormat)
3207 {
3208 BYTE *address = NULL;
3209 ULONG uWidth = 0;
3210 ULONG uHeight = 0;
3211 pFBInfo->pFramebuffer->COMGETTER(Width) (&uWidth);
3212 pFBInfo->pFramebuffer->COMGETTER(Height) (&uHeight);
3213 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
3214 if (SUCCEEDED(hrc) && address != NULL)
3215 {
3216 uint32_t width = pFBInfo->w;
3217 uint32_t height = pFBInfo->h;
3218
3219 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3220 int32_t xSrc = 0;
3221 int32_t ySrc = 0;
3222 uint32_t u32SrcWidth = pFBInfo->w;
3223 uint32_t u32SrcHeight = pFBInfo->h;
3224 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3225 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3226
3227 /* Default format is 32 bpp. */
3228 uint8_t *pu8Dst = address;
3229 int32_t xDst = xSrc;
3230 int32_t yDst = ySrc;
3231 uint32_t u32DstWidth = u32SrcWidth;
3232 uint32_t u32DstHeight = u32SrcHeight;
3233 uint32_t u32DstLineSize = u32DstWidth * 4;
3234 uint32_t u32DstBitsPerPixel = 32;
3235
3236 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
3237 * implies resize of Framebuffer is in progress and
3238 * copyrect should not be called.
3239 */
3240 if (uWidth == pFBInfo->w && uHeight == pFBInfo->h)
3241 {
3242
3243 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3244 width, height,
3245 pu8Src,
3246 xSrc, ySrc,
3247 u32SrcWidth, u32SrcHeight,
3248 u32SrcLineSize, u32SrcBitsPerPixel,
3249 pu8Dst,
3250 xDst, yDst,
3251 u32DstWidth, u32DstHeight,
3252 u32DstLineSize, u32DstBitsPerPixel);
3253 }
3254 }
3255 }
3256
3257 pDisplay->handleDisplayUpdate (uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
3258 }
3259 }
3260 if (!fUpdateAll)
3261 break;
3262 }
3263 pDisplay->vbvaUnlock();
3264}
3265
3266/**
3267 * Does a full invalidation of the VM display and instructs the VM
3268 * to update it immediately.
3269 *
3270 * @returns COM status code
3271 */
3272STDMETHODIMP Display::InvalidateAndUpdate()
3273{
3274 LogRelFlowFunc(("\n"));
3275
3276 AutoCaller autoCaller(this);
3277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3278
3279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 CHECK_CONSOLE_DRV(mpDrv);
3282
3283 Console::SafeVMPtr ptrVM(mParent);
3284 if (!ptrVM.isOk())
3285 return ptrVM.rc();
3286
3287 HRESULT rc = S_OK;
3288
3289 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
3290
3291 /* Have to release the lock when calling EMT. */
3292 alock.release();
3293
3294 /* pdm.h says that this has to be called from the EMT thread */
3295 int rcVBox = VMR3ReqCallVoidWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
3296 3, this, 0, true);
3297 alock.acquire();
3298
3299 if (RT_FAILURE(rcVBox))
3300 rc = setError(VBOX_E_IPRT_ERROR,
3301 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
3302
3303 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3304 return rc;
3305}
3306
3307/**
3308 * Notification that the framebuffer has completed the
3309 * asynchronous resize processing
3310 *
3311 * @returns COM status code
3312 */
3313STDMETHODIMP Display::ResizeCompleted(ULONG aScreenId)
3314{
3315 LogRelFlowFunc(("\n"));
3316
3317 /// @todo (dmik) can we AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); here?
3318 // This will require general code review and may add some details.
3319 // In particular, we may want to check whether EMT is really waiting for
3320 // this notification, etc. It might be also good to obey the caller to make
3321 // sure this method is not called from more than one thread at a time
3322 // (and therefore don't use Display lock at all here to save some
3323 // milliseconds).
3324 AutoCaller autoCaller(this);
3325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3326
3327 /* this is only valid for external framebuffers */
3328 if (maFramebuffers[aScreenId].pFramebuffer == NULL)
3329 return setError(VBOX_E_NOT_SUPPORTED,
3330 tr("Resize completed notification is valid only for external framebuffers"));
3331
3332 /* Set the flag indicating that the resize has completed and display
3333 * data need to be updated. */
3334 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[aScreenId].u32ResizeStatus,
3335 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
3336 AssertRelease(f);NOREF(f);
3337
3338 return S_OK;
3339}
3340
3341STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
3342{
3343#ifdef VBOX_WITH_VIDEOHWACCEL
3344 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsynch(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
3345 return S_OK;
3346#else
3347 return E_NOTIMPL;
3348#endif
3349}
3350
3351STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3352{
3353#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3354
3355 if (mcMonitors <= aScreenId)
3356 {
3357 AssertMsgFailed(("invalid screen id\n"));
3358 return E_INVALIDARG;
3359 }
3360
3361 BOOL is3denabled;
3362 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3363
3364 if (is3denabled)
3365 {
3366 VMMDev *pVMMDev = mParent->getVMMDev();
3367
3368 if (pVMMDev)
3369 {
3370 crViewportNotify(pVMMDev, aScreenId, x, y, width, height);
3371 }
3372 else
3373 {
3374 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
3375 pFb->pendingViewportInfo.fPending = true;
3376 pFb->pendingViewportInfo.x = x;
3377 pFb->pendingViewportInfo.y = y;
3378 pFb->pendingViewportInfo.width = width;
3379 pFb->pendingViewportInfo.height = height;
3380 }
3381 }
3382#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
3383 return S_OK;
3384}
3385
3386// private methods
3387/////////////////////////////////////////////////////////////////////////////
3388
3389/**
3390 * Helper to update the display information from the framebuffer.
3391 *
3392 * @thread EMT
3393 */
3394int Display::updateDisplayData(void)
3395{
3396 LogRelFlowFunc(("\n"));
3397
3398 /* the driver might not have been constructed yet */
3399 if (!mpDrv)
3400 return VINF_SUCCESS;
3401
3402#ifdef VBOX_STRICT
3403 /*
3404 * Sanity check. Note that this method may be called on EMT after Console
3405 * has started the power down procedure (but before our #drvDestruct() is
3406 * called, in which case pVM will already be NULL but mpDrv will not). Since
3407 * we don't really need pVM to proceed, we avoid this check in the release
3408 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
3409 * time-critical method.
3410 */
3411 Console::SafeVMPtrQuiet ptrVM(mParent);
3412 if (ptrVM.isOk())
3413 {
3414 PVM pVM = VMR3GetVM(ptrVM.rawUVM());
3415 Assert(VM_IS_EMT(pVM));
3416 }
3417#endif
3418
3419 /* The method is only relevant to the primary framebuffer. */
3420 IFramebuffer *pFramebuffer = maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer;
3421
3422 if (pFramebuffer)
3423 {
3424 HRESULT rc;
3425 BYTE *address = 0;
3426 rc = pFramebuffer->COMGETTER(Address) (&address);
3427 AssertComRC (rc);
3428 ULONG bytesPerLine = 0;
3429 rc = pFramebuffer->COMGETTER(BytesPerLine) (&bytesPerLine);
3430 AssertComRC (rc);
3431 ULONG bitsPerPixel = 0;
3432 rc = pFramebuffer->COMGETTER(BitsPerPixel) (&bitsPerPixel);
3433 AssertComRC (rc);
3434 ULONG width = 0;
3435 rc = pFramebuffer->COMGETTER(Width) (&width);
3436 AssertComRC (rc);
3437 ULONG height = 0;
3438 rc = pFramebuffer->COMGETTER(Height) (&height);
3439 AssertComRC (rc);
3440
3441 if ( (width != mLastWidth && mLastWidth != 0)
3442 || (height != mLastHeight && mLastHeight != 0))
3443 {
3444 LogRel(("updateDisplayData: size mismatch w %d(%d) h %d(%d)\n",
3445 width, mLastWidth, height, mLastHeight));
3446 return VERR_INVALID_STATE;
3447 }
3448
3449 mpDrv->IConnector.pu8Data = (uint8_t *) address;
3450 mpDrv->IConnector.cbScanline = bytesPerLine;
3451 mpDrv->IConnector.cBits = bitsPerPixel;
3452 mpDrv->IConnector.cx = width;
3453 mpDrv->IConnector.cy = height;
3454 }
3455 else
3456 {
3457 /* black hole */
3458 mpDrv->IConnector.pu8Data = NULL;
3459 mpDrv->IConnector.cbScanline = 0;
3460 mpDrv->IConnector.cBits = 0;
3461 mpDrv->IConnector.cx = 0;
3462 mpDrv->IConnector.cy = 0;
3463 }
3464 LogRelFlowFunc(("leave\n"));
3465 return VINF_SUCCESS;
3466}
3467
3468#ifdef VBOX_WITH_CROGL
3469void Display::crViewportNotify(VMMDev *pVMMDev, ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3470{
3471#if 0
3472 VBOXHGCMSVCPARM parm;
3473
3474 CRVBOXHGCMVIEWPORT *pViewportInfo = (CRVBOXHGCMVIEWPORT*)RTMemAlloc(sizeof (*pViewportInfo));
3475 if(!pViewportInfo)
3476 {
3477 AssertMsgFailed(("RTMemAlloc failed!\n"));
3478 return;
3479 }
3480
3481 pViewportInfo->u32Screen = aScreenId;
3482 pViewportInfo->x = x;
3483 pViewportInfo->y = y;
3484 pViewportInfo->width = width;
3485 pViewportInfo->height = height;
3486
3487 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3488 parm.u.pointer.addr = pViewportInfo;
3489 parm.u.pointer.size = sizeof (*pViewportInfo);
3490
3491 pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_VIEWPORT_CHANGED2, &parm, displayCrAsyncCmdCompletion, this);
3492#else
3493 VBOXHGCMSVCPARM aParms[5];
3494
3495 aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
3496 aParms[0].u.uint32 = aScreenId;
3497
3498 aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
3499 aParms[1].u.uint32 = x;
3500
3501 aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
3502 aParms[2].u.uint32 = y;
3503
3504
3505 aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
3506 aParms[3].u.uint32 = width;
3507
3508 aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
3509 aParms[4].u.uint32 = height;
3510
3511 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_VIEWPORT_CHANGED, SHCRGL_CPARMS_VIEWPORT_CHANGED, aParms);
3512#endif
3513}
3514#endif
3515
3516#ifdef VBOX_WITH_CRHGSMI
3517void Display::setupCrHgsmiData(void)
3518{
3519 VMMDev *pVMMDev = mParent->getVMMDev();
3520 Assert(pVMMDev);
3521 int rc = VERR_GENERAL_FAILURE;
3522 if (pVMMDev)
3523 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3524
3525 if (RT_SUCCESS(rc))
3526 {
3527 Assert(mhCrOglSvc);
3528 /* setup command completion callback */
3529 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
3530 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
3531 Completion.Hdr.cbCmd = sizeof (Completion);
3532 Completion.hCompletion = mpDrv->pVBVACallbacks;
3533 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3534
3535 VBOXHGCMSVCPARM parm;
3536 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3537 parm.u.pointer.addr = &Completion;
3538 parm.u.pointer.size = 0;
3539
3540 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3541 if (RT_SUCCESS(rc))
3542 {
3543 ULONG ul;
3544
3545 for (ul = 0; ul < mcMonitors; ul++)
3546 {
3547 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
3548 if (!pFb->pendingViewportInfo.fPending)
3549 continue;
3550
3551 crViewportNotify(pVMMDev, ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y, pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
3552 pFb->pendingViewportInfo.fPending = false;
3553 }
3554
3555 mCrOglCallbacks = Completion.MainInterface;
3556
3557 return;
3558 }
3559
3560 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed rc %d", rc));
3561 }
3562
3563 mhCrOglSvc = NULL;
3564}
3565
3566void Display::destructCrHgsmiData(void)
3567{
3568 mhCrOglSvc = NULL;
3569}
3570#endif
3571
3572/**
3573 * Changes the current frame buffer. Called on EMT to avoid both
3574 * race conditions and excessive locking.
3575 *
3576 * @note locks this object for writing
3577 * @thread EMT
3578 */
3579/* static */
3580DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
3581 unsigned uScreenId)
3582{
3583 LogRelFlowFunc(("uScreenId = %d\n", uScreenId));
3584
3585 AssertReturn(that, VERR_INVALID_PARAMETER);
3586 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
3587
3588 AutoCaller autoCaller(that);
3589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3590
3591 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
3592
3593 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
3594 pDisplayFBInfo->pFramebuffer = aFB;
3595
3596 that->mParent->consoleVRDPServer()->SendResize ();
3597
3598 /* The driver might not have been constructed yet */
3599 if (that->mpDrv)
3600 {
3601 /* Setup the new framebuffer, the resize will lead to an updateDisplayData call. */
3602 DISPLAYFBINFO *pFBInfo = &that->maFramebuffers[uScreenId];
3603
3604#if defined(VBOX_WITH_CROGL)
3605 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
3606 {
3607 BOOL is3denabled;
3608 that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3609
3610 if (is3denabled)
3611 {
3612 alock.release();
3613 }
3614 }
3615#endif
3616
3617 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
3618 {
3619 /* This display in VBVA mode. Resize it to the last guest resolution,
3620 * if it has been reported.
3621 */
3622 that->handleDisplayResize(uScreenId, pFBInfo->u16BitsPerPixel,
3623 pFBInfo->pu8FramebufferVRAM,
3624 pFBInfo->u32LineSize,
3625 pFBInfo->w,
3626 pFBInfo->h,
3627 pFBInfo->flags);
3628 }
3629 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3630 {
3631 /* VGA device mode, only for the primary screen. */
3632 that->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, that->mLastBitsPerPixel,
3633 that->mLastAddress,
3634 that->mLastBytesPerLine,
3635 that->mLastWidth,
3636 that->mLastHeight,
3637 that->mLastFlags);
3638 }
3639 }
3640
3641 LogRelFlowFunc(("leave\n"));
3642 return VINF_SUCCESS;
3643}
3644
3645/**
3646 * Handle display resize event issued by the VGA device for the primary screen.
3647 *
3648 * @see PDMIDISPLAYCONNECTOR::pfnResize
3649 */
3650DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
3651 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3652{
3653 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3654
3655 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
3656 bpp, pvVRAM, cbLine, cx, cy));
3657
3658 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
3659}
3660
3661/**
3662 * Handle display update.
3663 *
3664 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
3665 */
3666DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
3667 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3668{
3669 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3670
3671#ifdef DEBUG_sunlover
3672 LogFlowFunc(("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
3673 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
3674#endif /* DEBUG_sunlover */
3675
3676 /* This call does update regardless of VBVA status.
3677 * But in VBVA mode this is called only as result of
3678 * pfnUpdateDisplayAll in the VGA device.
3679 */
3680
3681 pDrv->pDisplay->handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3682}
3683
3684/**
3685 * Periodic display refresh callback.
3686 *
3687 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3688 * @thread EMT
3689 */
3690DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3691{
3692 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3693
3694#ifdef DEBUG_sunlover
3695 STAM_PROFILE_START(&g_StatDisplayRefresh, a);
3696#endif /* DEBUG_sunlover */
3697
3698#ifdef DEBUG_sunlover_2
3699 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3700 pDrv->pDisplay->mfVideoAccelEnabled));
3701#endif /* DEBUG_sunlover_2 */
3702
3703 Display *pDisplay = pDrv->pDisplay;
3704 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
3705 unsigned uScreenId;
3706
3707 Log2(("DisplayRefreshCallback\n"));
3708 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3709 {
3710 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3711
3712 /* Check the resize status. The status can be checked normally because
3713 * the status affects only the EMT.
3714 */
3715 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
3716
3717 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
3718 {
3719 LogRelFlowFunc(("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
3720 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
3721 /* The framebuffer was resized and display data need to be updated. */
3722 pDisplay->handleResizeCompletedEMT ();
3723 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
3724 {
3725 /* The resize status could be not Void here because a pending resize is issued. */
3726 continue;
3727 }
3728 /* Continue with normal processing because the status here is ResizeStatus_Void.
3729 * Repaint all displays because VM continued to run during the framebuffer resize.
3730 */
3731 pDisplay->InvalidateAndUpdateEMT(pDisplay, uScreenId, false);
3732 }
3733 else if (u32ResizeStatus == ResizeStatus_InProgress)
3734 {
3735 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
3736 LogRelFlowFunc(("ResizeStatus_InProcess\n"));
3737 fNoUpdate = true;
3738 continue;
3739 }
3740 }
3741
3742 if (!fNoUpdate)
3743 {
3744 int rc = pDisplay->videoAccelRefreshProcess();
3745 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
3746 {
3747 if (rc == VWRN_INVALID_STATE)
3748 {
3749 /* No VBVA do a display update. */
3750 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
3751 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3752 {
3753 Assert(pDrv->IConnector.pu8Data);
3754 pDisplay->vbvaLock();
3755 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
3756 pDisplay->vbvaUnlock();
3757 }
3758 }
3759
3760 /* Inform the VRDP server that the current display update sequence is
3761 * completed. At this moment the framebuffer memory contains a definite
3762 * image, that is synchronized with the orders already sent to VRDP client.
3763 * The server can now process redraw requests from clients or initial
3764 * fullscreen updates for new clients.
3765 */
3766 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3767 {
3768 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3769
3770 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3771 {
3772 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
3773 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
3774 }
3775 }
3776 }
3777 }
3778
3779#ifdef VBOX_WITH_VPX
3780 if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
3781 {
3782 do {
3783# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3784 BOOL is3denabled;
3785 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3786 if (is3denabled)
3787 {
3788 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
3789 {
3790 if (pDisplay->mCrOglCallbacks.pfnHasData())
3791 {
3792 /* submit */
3793
3794 VBOXHGCMSVCPARM parm;
3795
3796 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3797 parm.u.pointer.addr = &pDisplay->mCrOglScreenshotData;
3798 parm.u.pointer.size = sizeof (pDisplay->mCrOglScreenshotData);
3799
3800 VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
3801 if (pVMMDev)
3802 {
3803 int rc = pVMMDev->hgcmHostFastCallAsync(pDisplay->mhCrOglSvc, SHCRGL_HOST_FN_TAKE_SCREENSHOT, &parm, displayVRecCompletion, pDisplay);
3804 if (RT_SUCCESS(rc))
3805 break;
3806 else
3807 AssertMsgFailed(("hgcmHostFastCallAsync failed %f\n", rc));
3808 }
3809 else
3810 AssertMsgFailed(("no VMMDev\n"));
3811 }
3812
3813 /* no 3D data available, or error has occured,
3814 * go the straight way */
3815 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3816 }
3817 else
3818 {
3819 /* record request is still in progress, don't do anything */
3820 break;
3821 }
3822 }
3823# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3824
3825 uint64_t u64Now = RTTimeProgramMilliTS();
3826 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3827 {
3828 if (!pDisplay->maVideoRecEnabled[uScreenId])
3829 continue;
3830
3831 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3832
3833 if ( !pFBInfo->pFramebuffer.isNull()
3834 && !pFBInfo->fDisabled
3835 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3836 {
3837 int rc;
3838 if ( pFBInfo->fVBVAEnabled
3839 && pFBInfo->pu8FramebufferVRAM)
3840 {
3841 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3842 FramebufferPixelFormat_FOURCC_RGB,
3843 pFBInfo->u16BitsPerPixel,
3844 pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
3845 pFBInfo->pu8FramebufferVRAM, u64Now);
3846 }
3847 else
3848 {
3849 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3850 FramebufferPixelFormat_FOURCC_RGB,
3851 pDrv->IConnector.cBits,
3852 pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
3853 pDrv->IConnector.cy, pDrv->IConnector.pu8Data, u64Now);
3854 }
3855 if (rc == VINF_TRY_AGAIN)
3856 break;
3857 }
3858 }
3859 } while (0);
3860 }
3861#endif /* VBOX_WITH_VPX */
3862
3863#ifdef DEBUG_sunlover
3864 STAM_PROFILE_STOP(&g_StatDisplayRefresh, a);
3865#endif /* DEBUG_sunlover */
3866#ifdef DEBUG_sunlover_2
3867 LogFlowFunc(("leave\n"));
3868#endif /* DEBUG_sunlover_2 */
3869}
3870
3871/**
3872 * Reset notification
3873 *
3874 * @see PDMIDISPLAYCONNECTOR::pfnReset
3875 */
3876DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3877{
3878 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3879
3880 LogRelFlowFunc(("\n"));
3881
3882 /* Disable VBVA mode. */
3883 pDrv->pDisplay->VideoAccelEnable (false, NULL);
3884}
3885
3886/**
3887 * LFBModeChange notification
3888 *
3889 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3890 */
3891DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3892{
3893 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3894
3895 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3896
3897 NOREF(fEnabled);
3898
3899 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3900 /* The LFBModeChange function is called under DevVGA lock. Postpone disabling VBVA, do it in the refresh timer. */
3901 ASMAtomicWriteU32(&pDrv->pDisplay->mfu32PendingVideoAccelDisable, true);
3902}
3903
3904/**
3905 * Adapter information change notification.
3906 *
3907 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3908 */
3909DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, uint32_t u32VRAMSize)
3910{
3911 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3912
3913 if (pvVRAM == NULL)
3914 {
3915 unsigned i;
3916 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
3917 {
3918 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
3919
3920 pFBInfo->u32Offset = 0;
3921 pFBInfo->u32MaxFramebufferSize = 0;
3922 pFBInfo->u32InformationSize = 0;
3923 }
3924 }
3925#ifndef VBOX_WITH_HGSMI
3926 else
3927 {
3928 uint8_t *pu8 = (uint8_t *)pvVRAM;
3929 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
3930
3931 // @todo
3932 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
3933
3934 VBOXVIDEOINFOHDR *pHdr;
3935
3936 for (;;)
3937 {
3938 pHdr = (VBOXVIDEOINFOHDR *)pu8;
3939 pu8 += sizeof (VBOXVIDEOINFOHDR);
3940
3941 if (pu8 >= pu8End)
3942 {
3943 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
3944 break;
3945 }
3946
3947 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
3948 {
3949 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
3950 {
3951 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
3952 break;
3953 }
3954
3955 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
3956
3957 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
3958 {
3959 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
3960 break;
3961 }
3962
3963 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
3964
3965 pFBInfo->u32Offset = pDisplay->u32Offset;
3966 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
3967 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
3968
3969 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index, pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
3970 }
3971 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
3972 {
3973 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
3974 {
3975 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
3976 break;
3977 }
3978
3979 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
3980
3981 switch (pConf32->u32Index)
3982 {
3983 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
3984 {
3985 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
3986 } break;
3987
3988 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
3989 {
3990 /* @todo make configurable. */
3991 pConf32->u32Value = _1M;
3992 } break;
3993
3994 default:
3995 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
3996 }
3997 }
3998 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
3999 {
4000 if (pHdr->u16Length != 0)
4001 {
4002 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4003 break;
4004 }
4005
4006 break;
4007 }
4008 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP) /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo.cpp pushing this to us? */
4009 {
4010 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
4011 }
4012
4013 pu8 += pHdr->u16Length;
4014 }
4015 }
4016#endif /* !VBOX_WITH_HGSMI */
4017}
4018
4019/**
4020 * Display information change notification.
4021 *
4022 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
4023 */
4024DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
4025{
4026 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4027
4028 if (uScreenId >= pDrv->pDisplay->mcMonitors)
4029 {
4030 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
4031 return;
4032 }
4033
4034 /* Get the display information structure. */
4035 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
4036
4037 uint8_t *pu8 = (uint8_t *)pvVRAM;
4038 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
4039
4040 // @todo
4041 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
4042
4043 VBOXVIDEOINFOHDR *pHdr;
4044
4045 for (;;)
4046 {
4047 pHdr = (VBOXVIDEOINFOHDR *)pu8;
4048 pu8 += sizeof (VBOXVIDEOINFOHDR);
4049
4050 if (pu8 >= pu8End)
4051 {
4052 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
4053 break;
4054 }
4055
4056 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
4057 {
4058 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
4059 {
4060 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
4061 break;
4062 }
4063
4064 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
4065
4066 pFBInfo->xOrigin = pScreen->xOrigin;
4067 pFBInfo->yOrigin = pScreen->yOrigin;
4068
4069 pFBInfo->w = pScreen->u16Width;
4070 pFBInfo->h = pScreen->u16Height;
4071
4072 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
4073 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
4074
4075 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
4076 {
4077 /* Primary screen resize is eeeeeeeee by the VGA device. */
4078 if (pFBInfo->fDisabled)
4079 {
4080 pFBInfo->fDisabled = false;
4081 fireGuestMonitorChangedEvent(pDrv->pDisplay->mParent->getEventSource(),
4082 GuestMonitorChangedEventType_Enabled,
4083 uScreenId,
4084 pFBInfo->xOrigin, pFBInfo->yOrigin,
4085 pFBInfo->w, pFBInfo->h);
4086 }
4087
4088 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel, (uint8_t *)pvVRAM + pFBInfo->u32Offset, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, VBVA_SCREEN_F_ACTIVE);
4089 }
4090 }
4091 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
4092 {
4093 if (pHdr->u16Length != 0)
4094 {
4095 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4096 break;
4097 }
4098
4099 break;
4100 }
4101 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
4102 {
4103 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
4104 {
4105 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
4106 break;
4107 }
4108
4109 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
4110
4111 pFBInfo->pHostEvents = pHostEvents;
4112
4113 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
4114 pHostEvents));
4115 }
4116 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
4117 {
4118 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
4119 {
4120 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
4121 break;
4122 }
4123
4124 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
4125 pu8 += pLink->i32Offset;
4126 }
4127 else
4128 {
4129 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
4130 }
4131
4132 pu8 += pHdr->u16Length;
4133 }
4134}
4135
4136#ifdef VBOX_WITH_VIDEOHWACCEL
4137
4138#ifndef S_FALSE
4139# define S_FALSE ((HRESULT)1L)
4140#endif
4141
4142int Display::handleVHWACommandProcess(PVBOXVHWACMD pCommand)
4143{
4144 unsigned id = (unsigned)pCommand->iDisplay;
4145 int rc = VINF_SUCCESS;
4146 if (id >= mcMonitors)
4147 return VERR_INVALID_PARAMETER;
4148
4149 ComPtr<IFramebuffer> pFramebuffer;
4150 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4151 pFramebuffer = maFramebuffers[id].pFramebuffer;
4152 arlock.release();
4153
4154 if (pFramebuffer == NULL)
4155 return VERR_INVALID_STATE; /* notify we can not handle request atm */
4156
4157 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
4158 if (hr == S_FALSE)
4159 return VINF_SUCCESS;
4160 else if (SUCCEEDED(hr))
4161 return VINF_CALLBACK_RETURN;
4162 else if (hr == E_ACCESSDENIED)
4163 return VERR_INVALID_STATE; /* notify we can not handle request atm */
4164 else if (hr == E_NOTIMPL)
4165 return VERR_NOT_IMPLEMENTED;
4166 return VERR_GENERAL_FAILURE;
4167}
4168
4169DECLCALLBACK(int) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
4170{
4171 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4172
4173 return pDrv->pDisplay->handleVHWACommandProcess(pCommand);
4174}
4175#endif
4176
4177#ifdef VBOX_WITH_CRHGSMI
4178void Display::handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4179{
4180 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
4181}
4182
4183void Display::handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4184{
4185 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr, result);
4186}
4187
4188int Display::handleCrCmdNotifyCmds()
4189{
4190 int rc = VERR_INVALID_FUNCTION;
4191
4192 if (mhCrOglSvc)
4193 {
4194 VBOXHGCMSVCPARM dummy;
4195 VMMDev *pVMMDev = mParent->getVMMDev();
4196 if (pVMMDev)
4197 {
4198 /* no completion callback is specified with this call,
4199 * the CrOgl code will complete the CrHgsmi command once it processes it */
4200 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRCMD_NOTIFY_CMDS, &dummy, NULL, NULL);
4201 AssertRC(rc);
4202 }
4203 else
4204 rc = VERR_INVALID_STATE;
4205 }
4206
4207 return rc;
4208}
4209
4210void Display::handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
4211{
4212 int rc = VERR_INVALID_FUNCTION;
4213 VBOXHGCMSVCPARM parm;
4214 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4215 parm.u.pointer.addr = pCmd;
4216 parm.u.pointer.size = cbCmd;
4217
4218 if (mhCrOglSvc)
4219 {
4220 VMMDev *pVMMDev = mParent->getVMMDev();
4221 if (pVMMDev)
4222 {
4223 /* no completion callback is specified with this call,
4224 * the CrOgl code will complete the CrHgsmi command once it processes it */
4225 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
4226 AssertRC(rc);
4227 if (RT_SUCCESS(rc))
4228 return;
4229 }
4230 else
4231 rc = VERR_INVALID_STATE;
4232 }
4233
4234 /* we are here because something went wrong with command processing, complete it */
4235 handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
4236}
4237
4238void Display::handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
4239{
4240 int rc = VERR_INVALID_FUNCTION;
4241 VBOXHGCMSVCPARM parm;
4242 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4243 parm.u.pointer.addr = pCtl;
4244 parm.u.pointer.size = cbCtl;
4245
4246 if (mhCrOglSvc)
4247 {
4248 VMMDev *pVMMDev = mParent->getVMMDev();
4249 if (pVMMDev)
4250 {
4251 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm, Display::displayCrHgsmiControlCompletion, this);
4252 AssertRC(rc);
4253 if (RT_SUCCESS(rc))
4254 return;
4255 }
4256 else
4257 rc = VERR_INVALID_STATE;
4258 }
4259
4260 /* we are here because something went wrong with command processing, complete it */
4261 handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
4262}
4263
4264DECLCALLBACK(int) Display::displayCrCmdNotifyCmds(PPDMIDISPLAYCONNECTOR pInterface)
4265{
4266 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4267
4268 return pDrv->pDisplay->handleCrCmdNotifyCmds();
4269}
4270
4271DECLCALLBACK(void) Display::displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
4272{
4273 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4274
4275 pDrv->pDisplay->handleCrHgsmiCommandProcess(pCmd, cbCmd);
4276}
4277
4278DECLCALLBACK(void) Display::displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd)
4279{
4280 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4281
4282 pDrv->pDisplay->handleCrHgsmiControlProcess(pCmd, cbCmd);
4283}
4284
4285DECLCALLBACK(void) Display::displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4286{
4287 AssertMsgFailed(("not expected!"));
4288 Display *pDisplay = (Display *)pvContext;
4289 pDisplay->handleCrHgsmiCommandCompletion(result, u32Function, pParam);
4290}
4291
4292DECLCALLBACK(void) Display::displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4293{
4294 Display *pDisplay = (Display *)pvContext;
4295 pDisplay->handleCrHgsmiControlCompletion(result, u32Function, pParam);
4296}
4297#endif
4298
4299#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4300DECLCALLBACK(void) Display::displayCrAsyncCmdCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4301{
4302 Display *pDisplay = (Display *)pvContext;
4303 pDisplay->handleCrAsyncCmdCompletion(result, u32Function, pParam);
4304}
4305
4306
4307void Display::handleCrAsyncCmdCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4308{
4309 if (pParam->type == VBOX_HGCM_SVC_PARM_PTR && pParam->u.pointer.addr)
4310 RTMemFree(pParam->u.pointer.addr);
4311}
4312
4313bool Display::handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64TimeStamp)
4314{
4315# if VBOX_WITH_VPX
4316 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64TimeStamp);
4317# else
4318 return false;
4319# endif
4320}
4321
4322void Display::handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64TimeStamp)
4323{
4324}
4325
4326void Display::handleCrVRecScreenshotPerform(uint32_t uScreen,
4327 uint32_t x, uint32_t y, uint32_t uPixelFormat,
4328 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4329 uint32_t uGuestWidth, uint32_t uGuestHeight,
4330 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4331{
4332 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4333# if VBOX_WITH_VPX
4334 int rc = VideoRecCopyToIntBuf(mpVideoRecCtx, uScreen, x, y,
4335 uPixelFormat,
4336 uBitsPerPixel, uBytesPerLine,
4337 uGuestWidth, uGuestHeight,
4338 pu8BufferAddress, u64TimeStamp);
4339 Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
4340# endif
4341}
4342
4343void Display::handleVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4344{
4345 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4346 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4347}
4348
4349DECLCALLBACK(void) Display::displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
4350 uint32_t x, uint32_t y,
4351 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4352 uint32_t uGuestWidth, uint32_t uGuestHeight,
4353 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4354{
4355 Display *pDisplay = (Display *)pvCtx;
4356 pDisplay->handleCrVRecScreenshotPerform(uScreen,
4357 x, y, FramebufferPixelFormat_FOURCC_RGB, uBitsPerPixel,
4358 uBytesPerLine, uGuestWidth, uGuestHeight,
4359 pu8BufferAddress, u64TimeStamp);
4360}
4361
4362DECLCALLBACK(bool) Display::displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4363{
4364 Display *pDisplay = (Display *)pvCtx;
4365 return pDisplay->handleCrVRecScreenshotBegin(uScreen, u64TimeStamp);
4366}
4367
4368DECLCALLBACK(void) Display::displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4369{
4370 Display *pDisplay = (Display *)pvCtx;
4371 pDisplay->handleCrVRecScreenshotEnd(uScreen, u64TimeStamp);
4372}
4373
4374DECLCALLBACK(void) Display::displayVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4375{
4376 Display *pDisplay = (Display *)pvContext;
4377 pDisplay->handleVRecCompletion(result, u32Function, pParam, pvContext);
4378}
4379
4380#endif
4381
4382
4383#ifdef VBOX_WITH_HGSMI
4384DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags)
4385{
4386 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4387
4388 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4389 Display *pThis = pDrv->pDisplay;
4390
4391 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
4392 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
4393 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
4394
4395 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
4396
4397 return VINF_SUCCESS;
4398}
4399
4400DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4401{
4402 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4403
4404 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4405 Display *pThis = pDrv->pDisplay;
4406
4407 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4408
4409 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4410 {
4411 /* Make sure that the primary screen is visible now.
4412 * The guest can't use VBVA anymore, so only only the VGA device output works.
4413 */
4414 if (pFBInfo->fDisabled)
4415 {
4416 pFBInfo->fDisabled = false;
4417 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4418 GuestMonitorChangedEventType_Enabled,
4419 uScreenId,
4420 pFBInfo->xOrigin, pFBInfo->yOrigin,
4421 pFBInfo->w, pFBInfo->h);
4422 }
4423 }
4424
4425 pFBInfo->fVBVAEnabled = false;
4426 pFBInfo->fVBVAForceResize = false;
4427
4428 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
4429
4430 pFBInfo->pVBVAHostFlags = NULL;
4431}
4432
4433DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4434{
4435 LogFlowFunc(("uScreenId %d\n", uScreenId));
4436
4437 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4438 Display *pThis = pDrv->pDisplay;
4439 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4440
4441 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
4442 {
4443 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers, pThis->mcMonitors);
4444 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
4445 }
4446
4447 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
4448 {
4449 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
4450 {
4451 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
4452 * under display device lock, so thread safe.
4453 */
4454 pFBInfo->cVBVASkipUpdate = 0;
4455 pThis->handleDisplayUpdate(uScreenId, pFBInfo->vbvaSkippedRect.xLeft - pFBInfo->xOrigin,
4456 pFBInfo->vbvaSkippedRect.yTop - pFBInfo->yOrigin,
4457 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
4458 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
4459 }
4460 }
4461 else
4462 {
4463 /* The framebuffer is being resized. */
4464 pFBInfo->cVBVASkipUpdate++;
4465 }
4466}
4467
4468DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, const PVBVACMDHDR pCmd, size_t cbCmd)
4469{
4470 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
4471
4472 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4473 Display *pThis = pDrv->pDisplay;
4474 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4475
4476 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
4477 {
4478 if (pFBInfo->fDefaultFormat)
4479 {
4480 /* Make sure that framebuffer contains the same image as the guest VRAM. */
4481 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
4482 && !pFBInfo->pFramebuffer.isNull()
4483 && !pFBInfo->fDisabled)
4484 {
4485 pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
4486 }
4487 else if ( !pFBInfo->pFramebuffer.isNull()
4488 && !pFBInfo->fDisabled)
4489 {
4490 /* Render VRAM content to the framebuffer. */
4491 BYTE *address = NULL;
4492 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
4493 if (SUCCEEDED(hrc) && address != NULL)
4494 {
4495 uint32_t width = pCmd->w;
4496 uint32_t height = pCmd->h;
4497
4498 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
4499 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
4500 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
4501 uint32_t u32SrcWidth = pFBInfo->w;
4502 uint32_t u32SrcHeight = pFBInfo->h;
4503 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
4504 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
4505
4506 uint8_t *pu8Dst = address;
4507 int32_t xDst = xSrc;
4508 int32_t yDst = ySrc;
4509 uint32_t u32DstWidth = u32SrcWidth;
4510 uint32_t u32DstHeight = u32SrcHeight;
4511 uint32_t u32DstLineSize = u32DstWidth * 4;
4512 uint32_t u32DstBitsPerPixel = 32;
4513
4514 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
4515 width, height,
4516 pu8Src,
4517 xSrc, ySrc,
4518 u32SrcWidth, u32SrcHeight,
4519 u32SrcLineSize, u32SrcBitsPerPixel,
4520 pu8Dst,
4521 xDst, yDst,
4522 u32DstWidth, u32DstHeight,
4523 u32DstLineSize, u32DstBitsPerPixel);
4524 }
4525 }
4526 }
4527
4528 VBVACMDHDR hdrSaved = *pCmd;
4529
4530 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
4531
4532 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
4533 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
4534
4535 /* @todo new SendUpdate entry which can get a separate cmd header or coords. */
4536 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, (uint32_t)cbCmd);
4537
4538 *pHdrUnconst = hdrSaved;
4539 }
4540}
4541
4542DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
4543{
4544 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
4545
4546 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4547 Display *pThis = pDrv->pDisplay;
4548 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4549
4550 /* @todo handleFramebufferUpdate (uScreenId,
4551 * x - pThis->maFramebuffers[uScreenId].xOrigin,
4552 * y - pThis->maFramebuffers[uScreenId].yOrigin,
4553 * cx, cy);
4554 */
4555 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
4556 {
4557 pThis->handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
4558 }
4559 else
4560 {
4561 /* Save the updated rectangle. */
4562 int32_t xRight = x + cx;
4563 int32_t yBottom = y + cy;
4564
4565 if (pFBInfo->cVBVASkipUpdate == 1)
4566 {
4567 pFBInfo->vbvaSkippedRect.xLeft = x;
4568 pFBInfo->vbvaSkippedRect.yTop = y;
4569 pFBInfo->vbvaSkippedRect.xRight = xRight;
4570 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
4571 }
4572 else
4573 {
4574 if (pFBInfo->vbvaSkippedRect.xLeft > x)
4575 {
4576 pFBInfo->vbvaSkippedRect.xLeft = x;
4577 }
4578 if (pFBInfo->vbvaSkippedRect.yTop > y)
4579 {
4580 pFBInfo->vbvaSkippedRect.yTop = y;
4581 }
4582 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
4583 {
4584 pFBInfo->vbvaSkippedRect.xRight = xRight;
4585 }
4586 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
4587 {
4588 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
4589 }
4590 }
4591 }
4592}
4593
4594#ifdef DEBUG_sunlover
4595static void logVBVAResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
4596{
4597 LogRel(("displayVBVAResize: [%d] %s\n"
4598 " pView->u32ViewIndex %d\n"
4599 " pView->u32ViewOffset 0x%08X\n"
4600 " pView->u32ViewSize 0x%08X\n"
4601 " pView->u32MaxScreenSize 0x%08X\n"
4602 " pScreen->i32OriginX %d\n"
4603 " pScreen->i32OriginY %d\n"
4604 " pScreen->u32StartOffset 0x%08X\n"
4605 " pScreen->u32LineSize 0x%08X\n"
4606 " pScreen->u32Width %d\n"
4607 " pScreen->u32Height %d\n"
4608 " pScreen->u16BitsPerPixel %d\n"
4609 " pScreen->u16Flags 0x%04X\n"
4610 " pFBInfo->u32Offset 0x%08X\n"
4611 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
4612 " pFBInfo->u32InformationSize 0x%08X\n"
4613 " pFBInfo->fDisabled %d\n"
4614 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
4615 " pFBInfo->u16BitsPerPixel %d\n"
4616 " pFBInfo->pu8FramebufferVRAM %p\n"
4617 " pFBInfo->u32LineSize 0x%08X\n"
4618 " pFBInfo->flags 0x%04X\n"
4619 " pFBInfo->pHostEvents %p\n"
4620 " pFBInfo->u32ResizeStatus %d\n"
4621 " pFBInfo->fDefaultFormat %d\n"
4622 " dirtyRect %d-%d %d-%d\n"
4623 " pFBInfo->pendingResize.fPending %d\n"
4624 " pFBInfo->pendingResize.pixelFormat %d\n"
4625 " pFBInfo->pendingResize.pvVRAM %p\n"
4626 " pFBInfo->pendingResize.bpp %d\n"
4627 " pFBInfo->pendingResize.cbLine 0x%08X\n"
4628 " pFBInfo->pendingResize.w,h %dx%d\n"
4629 " pFBInfo->pendingResize.flags 0x%04X\n"
4630 " pFBInfo->fVBVAEnabled %d\n"
4631 " pFBInfo->fVBVAForceResize %d\n"
4632 " pFBInfo->cVBVASkipUpdate %d\n"
4633 " pFBInfo->vbvaSkippedRect %d-%d %d-%d\n"
4634 " pFBInfo->pVBVAHostFlags %p\n"
4635 "",
4636 pScreen->u32ViewIndex,
4637 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
4638 pView->u32ViewIndex,
4639 pView->u32ViewOffset,
4640 pView->u32ViewSize,
4641 pView->u32MaxScreenSize,
4642 pScreen->i32OriginX,
4643 pScreen->i32OriginY,
4644 pScreen->u32StartOffset,
4645 pScreen->u32LineSize,
4646 pScreen->u32Width,
4647 pScreen->u32Height,
4648 pScreen->u16BitsPerPixel,
4649 pScreen->u16Flags,
4650 pFBInfo->u32Offset,
4651 pFBInfo->u32MaxFramebufferSize,
4652 pFBInfo->u32InformationSize,
4653 pFBInfo->fDisabled,
4654 pFBInfo->xOrigin,
4655 pFBInfo->yOrigin,
4656 pFBInfo->w,
4657 pFBInfo->h,
4658 pFBInfo->u16BitsPerPixel,
4659 pFBInfo->pu8FramebufferVRAM,
4660 pFBInfo->u32LineSize,
4661 pFBInfo->flags,
4662 pFBInfo->pHostEvents,
4663 pFBInfo->u32ResizeStatus,
4664 pFBInfo->fDefaultFormat,
4665 pFBInfo->dirtyRect.xLeft,
4666 pFBInfo->dirtyRect.xRight,
4667 pFBInfo->dirtyRect.yTop,
4668 pFBInfo->dirtyRect.yBottom,
4669 pFBInfo->pendingResize.fPending,
4670 pFBInfo->pendingResize.pixelFormat,
4671 pFBInfo->pendingResize.pvVRAM,
4672 pFBInfo->pendingResize.bpp,
4673 pFBInfo->pendingResize.cbLine,
4674 pFBInfo->pendingResize.w,
4675 pFBInfo->pendingResize.h,
4676 pFBInfo->pendingResize.flags,
4677 pFBInfo->fVBVAEnabled,
4678 pFBInfo->fVBVAForceResize,
4679 pFBInfo->cVBVASkipUpdate,
4680 pFBInfo->vbvaSkippedRect.xLeft,
4681 pFBInfo->vbvaSkippedRect.yTop,
4682 pFBInfo->vbvaSkippedRect.xRight,
4683 pFBInfo->vbvaSkippedRect.yBottom,
4684 pFBInfo->pVBVAHostFlags
4685 ));
4686}
4687#endif /* DEBUG_sunlover */
4688
4689DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
4690{
4691 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
4692
4693 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4694 Display *pThis = pDrv->pDisplay;
4695
4696 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
4697
4698 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
4699 {
4700 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
4701
4702 pFBInfo->fDisabled = true;
4703 pFBInfo->flags = pScreen->u16Flags;
4704
4705 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
4706 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
4707 * the VM window will be black. */
4708 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
4709 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
4710 pThis->handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
4711 u32Width, u32Height, pScreen->u16Flags);
4712
4713 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4714 GuestMonitorChangedEventType_Disabled,
4715 pScreen->u32ViewIndex,
4716 0, 0, 0, 0);
4717 return VINF_SUCCESS;
4718 }
4719
4720 /* If display was disabled or there is no framebuffer, a resize will be required,
4721 * because the framebuffer was/will be changed.
4722 */
4723 bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
4724
4725 if (pFBInfo->fVBVAForceResize)
4726 {
4727 /* VBVA was just enabled. Do the resize. */
4728 fResize = true;
4729 pFBInfo->fVBVAForceResize = false;
4730 }
4731
4732 /* Check if this is a real resize or a notification about the screen origin.
4733 * The guest uses this VBVAResize call for both.
4734 */
4735 fResize = fResize
4736 || pFBInfo->u16BitsPerPixel != pScreen->u16BitsPerPixel
4737 || pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM + pScreen->u32StartOffset
4738 || pFBInfo->u32LineSize != pScreen->u32LineSize
4739 || pFBInfo->w != pScreen->u32Width
4740 || pFBInfo->h != pScreen->u32Height;
4741
4742 bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
4743 || pFBInfo->yOrigin != pScreen->i32OriginY;
4744
4745 if (fNewOrigin || fResize)
4746 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
4747
4748 if (pFBInfo->fDisabled)
4749 {
4750 pFBInfo->fDisabled = false;
4751 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4752 GuestMonitorChangedEventType_Enabled,
4753 pScreen->u32ViewIndex,
4754 pScreen->i32OriginX, pScreen->i32OriginY,
4755 pScreen->u32Width, pScreen->u32Height);
4756 /* Continue to update pFBInfo. */
4757 }
4758
4759 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
4760 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
4761 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
4762
4763 pFBInfo->xOrigin = pScreen->i32OriginX;
4764 pFBInfo->yOrigin = pScreen->i32OriginY;
4765
4766 pFBInfo->w = pScreen->u32Width;
4767 pFBInfo->h = pScreen->u32Height;
4768
4769 pFBInfo->u16BitsPerPixel = pScreen->u16BitsPerPixel;
4770 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM + pScreen->u32StartOffset;
4771 pFBInfo->u32LineSize = pScreen->u32LineSize;
4772
4773 pFBInfo->flags = pScreen->u16Flags;
4774
4775 if (fNewOrigin)
4776 {
4777 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4778 GuestMonitorChangedEventType_NewOrigin,
4779 pScreen->u32ViewIndex,
4780 pScreen->i32OriginX, pScreen->i32OriginY,
4781 0, 0);
4782 }
4783
4784 if (!fResize)
4785 {
4786 /* No parameters of the framebuffer have actually changed. */
4787 if (fNewOrigin)
4788 {
4789 /* VRDP server still need this notification. */
4790 LogRelFlowFunc(("Calling VRDP\n"));
4791 pThis->mParent->consoleVRDPServer()->SendResize();
4792 }
4793 return VINF_SUCCESS;
4794 }
4795
4796 if (pFBInfo->pFramebuffer.isNull())
4797 {
4798 /* If no framebuffer, the resize will be done later when a new framebuffer will be set in changeFramebuffer. */
4799 return VINF_SUCCESS;
4800 }
4801
4802 /* If the framebuffer already set for the screen, do a regular resize. */
4803 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
4804 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
4805 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags);
4806}
4807
4808DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
4809 uint32_t xHot, uint32_t yHot,
4810 uint32_t cx, uint32_t cy,
4811 const void *pvShape)
4812{
4813 LogFlowFunc(("\n"));
4814
4815 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4816 Display *pThis = pDrv->pDisplay;
4817
4818 size_t cbShapeSize = 0;
4819
4820 if (pvShape)
4821 {
4822 cbShapeSize = (cx + 7) / 8 * cy; /* size of the AND mask */
4823 cbShapeSize = ((cbShapeSize + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
4824 }
4825 com::SafeArray<BYTE> shapeData(cbShapeSize);
4826
4827 if (pvShape)
4828 ::memcpy(shapeData.raw(), pvShape, cbShapeSize);
4829
4830 /* Tell the console about it */
4831 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
4832 xHot, yHot, cx, cy, ComSafeArrayAsInParam(shapeData));
4833
4834 return VINF_SUCCESS;
4835}
4836#endif /* VBOX_WITH_HGSMI */
4837
4838/**
4839 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4840 */
4841DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4842{
4843 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4844 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4845 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4846 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
4847 return NULL;
4848}
4849
4850
4851/**
4852 * Destruct a display driver instance.
4853 *
4854 * @returns VBox status.
4855 * @param pDrvIns The driver instance data.
4856 */
4857DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
4858{
4859 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4860 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4861 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4862
4863 if (pThis->pDisplay)
4864 {
4865 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
4866#ifdef VBOX_WITH_VPX
4867 pThis->pDisplay->VideoCaptureStop();
4868#endif
4869#ifdef VBOX_WITH_CRHGSMI
4870 pThis->pDisplay->destructCrHgsmiData();
4871#endif
4872 pThis->pDisplay->mpDrv = NULL;
4873 pThis->pDisplay->mpVMMDev = NULL;
4874 pThis->pDisplay->mLastAddress = NULL;
4875 pThis->pDisplay->mLastBytesPerLine = 0;
4876 pThis->pDisplay->mLastBitsPerPixel = 0,
4877 pThis->pDisplay->mLastWidth = 0;
4878 pThis->pDisplay->mLastHeight = 0;
4879 }
4880}
4881
4882
4883/**
4884 * Construct a display driver instance.
4885 *
4886 * @copydoc FNPDMDRVCONSTRUCT
4887 */
4888DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4889{
4890 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4891 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4892 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4893
4894 /*
4895 * Validate configuration.
4896 */
4897 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
4898 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4899 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
4900 ("Configuration error: Not possible to attach anything to this driver!\n"),
4901 VERR_PDM_DRVINS_NO_ATTACH);
4902
4903 /*
4904 * Init Interfaces.
4905 */
4906 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
4907
4908 pThis->IConnector.pfnResize = Display::displayResizeCallback;
4909 pThis->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
4910 pThis->IConnector.pfnRefresh = Display::displayRefreshCallback;
4911 pThis->IConnector.pfnReset = Display::displayResetCallback;
4912 pThis->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
4913 pThis->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
4914 pThis->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
4915#ifdef VBOX_WITH_VIDEOHWACCEL
4916 pThis->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
4917#endif
4918#ifdef VBOX_WITH_CRHGSMI
4919 pThis->IConnector.pfnCrCmdNotifyCmds = Display::displayCrCmdNotifyCmds;
4920 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
4921 pThis->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
4922#endif
4923#ifdef VBOX_WITH_HGSMI
4924 pThis->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
4925 pThis->IConnector.pfnVBVADisable = Display::displayVBVADisable;
4926 pThis->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
4927 pThis->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
4928 pThis->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
4929 pThis->IConnector.pfnVBVAResize = Display::displayVBVAResize;
4930 pThis->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
4931#endif
4932
4933 /*
4934 * Get the IDisplayPort interface of the above driver/device.
4935 */
4936 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
4937 if (!pThis->pUpPort)
4938 {
4939 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
4940 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4941 }
4942#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
4943 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
4944 if (!pThis->pVBVACallbacks)
4945 {
4946 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
4947 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4948 }
4949#endif
4950 /*
4951 * Get the Display object pointer and update the mpDrv member.
4952 */
4953 void *pv;
4954 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
4955 if (RT_FAILURE(rc))
4956 {
4957 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
4958 return rc;
4959 }
4960 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
4961 pThis->pDisplay = pDisplay;
4962 pThis->pDisplay->mpDrv = pThis;
4963 /*
4964 * Update our display information according to the framebuffer
4965 */
4966 pDisplay->updateDisplayData();
4967
4968 /*
4969 * Start periodic screen refreshes
4970 */
4971 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
4972
4973#ifdef VBOX_WITH_CRHGSMI
4974 pDisplay->setupCrHgsmiData();
4975#endif
4976
4977#ifdef VBOX_WITH_VPX
4978 ComPtr<IMachine> pMachine = pDisplay->mParent->machine();
4979 BOOL fEnabled = false;
4980 HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
4981 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
4982 if (fEnabled)
4983 {
4984 rc = pDisplay->VideoCaptureStart();
4985 fireVideoCaptureChangedEvent(pDisplay->mParent->getEventSource());
4986 }
4987#endif
4988
4989 return rc;
4990}
4991
4992
4993/**
4994 * Display driver registration record.
4995 */
4996const PDMDRVREG Display::DrvReg =
4997{
4998 /* u32Version */
4999 PDM_DRVREG_VERSION,
5000 /* szName */
5001 "MainDisplay",
5002 /* szRCMod */
5003 "",
5004 /* szR0Mod */
5005 "",
5006 /* pszDescription */
5007 "Main display driver (Main as in the API).",
5008 /* fFlags */
5009 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5010 /* fClass. */
5011 PDM_DRVREG_CLASS_DISPLAY,
5012 /* cMaxInstances */
5013 ~0U,
5014 /* cbInstance */
5015 sizeof(DRVMAINDISPLAY),
5016 /* pfnConstruct */
5017 Display::drvConstruct,
5018 /* pfnDestruct */
5019 Display::drvDestruct,
5020 /* pfnRelocate */
5021 NULL,
5022 /* pfnIOCtl */
5023 NULL,
5024 /* pfnPowerOn */
5025 NULL,
5026 /* pfnReset */
5027 NULL,
5028 /* pfnSuspend */
5029 NULL,
5030 /* pfnResume */
5031 NULL,
5032 /* pfnAttach */
5033 NULL,
5034 /* pfnDetach */
5035 NULL,
5036 /* pfnPowerOff */
5037 NULL,
5038 /* pfnSoftReset */
5039 NULL,
5040 /* u32EndVersion */
5041 PDM_DRVREG_VERSION
5042};
5043/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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