VirtualBox

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

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

Main/VPX: do uninitialization in drvDestruct() rather than in uninit()

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

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