VirtualBox

source: vbox/trunk/src/VBox/Main/DisplayImpl.cpp@ 32846

Last change on this file since 32846 was 32405, checked in by vboxsync, 14 years ago

Main: make it right this time

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

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