VirtualBox

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

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

Main: fix win

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