VirtualBox

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

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

Main/Display: store mode data in case of a null framebuffer

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

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