VirtualBox

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

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

Main/DisplayImpl: 6711: Fix for the race condition where in GA sends the new resize rectangle dimensions while framebuffer is still resizing. At this moment DisplayImpl doesn't have the updated framebuffer rectangles and as a result the intersection between the new rectangles passed by GA and current frambuffe is NULL. Thus DisplayImpl rejects the rectangles passed from GA and GUI receives NULL for drawing.
The fix for the issue is to let DisplayImpl save the rectangles passed from the GA till the point framebuffer is completely resized and then pass these rectangle to GUI after checking for intersection with the curent newly resized framebuffer.

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

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