VirtualBox

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

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

Main: not necessary

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