VirtualBox

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

Last change on this file since 51553 was 51553, checked in by vboxsync, 11 years ago

Main: removed obsolete SetFramebuffer method.

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