VirtualBox

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

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

DisplayImpl: fix and cleanup for postponed seamless region update

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