VirtualBox

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

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

Main: removed obsolete RequestResize, ResizeCompleted methods.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 185.0 KB
Line 
1/* $Id: DisplayImpl.cpp 51551 2014-06-05 12:09:12Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "DisplayImpl.h"
19#include "DisplayUtils.h"
20#include "ConsoleImpl.h"
21#include "ConsoleVRDPServer.h"
22#include "VMMDev.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27/* generated header */
28#include "VBoxEvents.h"
29
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/asm.h>
33#include <iprt/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::SetFramebuffer(ULONG aScreenId, IFramebuffer *aFramebuffer)
2443{
2444 LogRelFlowFunc(("\n"));
2445
2446 if (aFramebuffer != NULL)
2447 CheckComArgOutPointerValid(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 Console::SafeVMPtrQuiet ptrVM(mParent);
2456 if (ptrVM.isOk())
2457 {
2458 /* Must release the lock here because the changeFramebuffer will
2459 * also obtain it. */
2460 alock.release();
2461
2462 /* send request to the EMT thread */
2463 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
2464 (PFNRT)changeFramebuffer, 3, this, aFramebuffer, aScreenId);
2465
2466 ComAssertRCRet(vrc, E_FAIL);
2467 alock.acquire();
2468
2469#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2470 BOOL fIs3DEnabled;
2471 HRESULT hr = mParent->machine()->COMGETTER(Accelerate3DEnabled)(&fIs3DEnabled);
2472 ComAssertComRC(hr);
2473 if (fIs3DEnabled)
2474 {
2475 VBOXCRCMDCTL_HGCM data;
2476 RT_ZERO(data);
2477 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2478 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
2479
2480 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
2481 data.aParms[0].u.uint32 = aScreenId;
2482
2483 alock.release();
2484
2485 vrc = crCtlSubmitSync(&data.Hdr, sizeof(data));
2486 AssertRC(vrc);
2487
2488 alock.acquire();
2489 }
2490#endif /* #if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
2491 }
2492 else
2493 {
2494 /* No VM is created (VM is powered off), do a direct call */
2495 int vrc = changeFramebuffer(this, aFramebuffer, aScreenId);
2496 ComAssertRCRet(vrc, E_FAIL);
2497 }
2498
2499 return S_OK;
2500}
2501
2502STDMETHODIMP Display::AttachFramebuffer(ULONG aScreenId,
2503 IFramebuffer *aFramebuffer)
2504{
2505 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2506
2507 CheckComArgPointerValid(aFramebuffer);
2508
2509 AutoCaller autoCaller(this);
2510 if (FAILED(autoCaller.rc()))
2511 return autoCaller.rc();
2512
2513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 if (aScreenId >= mcMonitors)
2516 return setError(E_INVALIDARG, tr("AttachFramebuffer: Invalid screen %d (total %d)"),
2517 aScreenId, mcMonitors);
2518
2519 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2520 if (!pFBInfo->pFramebuffer.isNull())
2521 return setError(E_FAIL, tr("AttachFramebuffer: Framebuffer already attached to %d"),
2522 aScreenId);
2523
2524 pFBInfo->pFramebuffer = aFramebuffer;
2525
2526 /* The driver might not have been constructed yet */
2527 if (mpDrv)
2528 {
2529 /* Setup the new framebuffer, the resize will lead to an updateDisplayData call. */
2530
2531#if defined(VBOX_WITH_CROGL)
2532 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
2533 /* @todo investigate */
2534 {
2535 BOOL is3denabled;
2536 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2537
2538 if (is3denabled)
2539 {
2540 alock.release();
2541 }
2542 }
2543#endif
2544
2545 /* @todo generic code for all monitors. */
2546 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
2547 {
2548 /* This display in VBVA mode. Resize it to the last guest resolution,
2549 * if it has been reported.
2550 */
2551 handleDisplayResize(aScreenId, pFBInfo->u16BitsPerPixel,
2552 pFBInfo->pu8FramebufferVRAM,
2553 pFBInfo->u32LineSize,
2554 pFBInfo->w,
2555 pFBInfo->h,
2556 pFBInfo->flags);
2557 }
2558 else if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2559 {
2560 /* VGA device mode, only for the primary screen. */
2561 handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, mLastBitsPerPixel,
2562 mLastAddress,
2563 mLastBytesPerLine,
2564 mLastWidth,
2565 mLastHeight,
2566 mLastFlags);
2567 }
2568
2569 alock.release();
2570
2571 Console::SafeVMPtrQuiet ptrVM(mParent);
2572 if (ptrVM.isOk())
2573 {
2574 VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
2575 3, this, aScreenId, false);
2576 }
2577 }
2578
2579 LogRelFlowFunc(("Attached to %d\n", aScreenId));
2580 return S_OK;
2581}
2582
2583STDMETHODIMP Display::DetachFramebuffer(ULONG aScreenId)
2584{
2585 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2586
2587 AutoCaller autoCaller(this);
2588 if (FAILED(autoCaller.rc()))
2589 return autoCaller.rc();
2590
2591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 if (aScreenId >= mcMonitors)
2594 return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
2595 aScreenId, mcMonitors);
2596
2597 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2598
2599 pFBInfo->pFramebuffer.setNull();
2600
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Display::QueryFramebuffer(ULONG aScreenId,
2605 IFramebuffer **aFramebuffer)
2606{
2607 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2608
2609 CheckComArgOutPointerValid(aFramebuffer);
2610
2611 AutoCaller autoCaller(this);
2612 if (FAILED(autoCaller.rc()))
2613 return autoCaller.rc();
2614
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 if (aScreenId >= mcMonitors)
2618 return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
2619 aScreenId, mcMonitors);
2620
2621 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2622
2623 *aFramebuffer = pFBInfo->pFramebuffer;
2624 if (!pFBInfo->pFramebuffer.isNull())
2625 pFBInfo->pFramebuffer->AddRef();
2626
2627 return S_OK;
2628}
2629
2630STDMETHODIMP Display::GetFramebuffer(ULONG aScreenId,
2631 IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
2632{
2633 LogRelFlowFunc(("aScreenId=%ld\n", aScreenId));
2634
2635 CheckComArgOutPointerValid(aFramebuffer);
2636
2637 AutoCaller autoCaller(this);
2638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2639
2640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 if (aScreenId != 0 && aScreenId >= mcMonitors)
2643 return E_INVALIDARG;
2644
2645 /** @todo This should be actually done on EMT. */
2646 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2647 AssertPtr(pFBInfo);
2648
2649 *aFramebuffer = pFBInfo->pFramebuffer; /** @todo r=andy Race prone? Use a ComPtr instead? */
2650 if (*aFramebuffer)
2651 (*aFramebuffer)->AddRef();
2652 if (aXOrigin)
2653 *aXOrigin = pFBInfo->xOrigin;
2654 if (aYOrigin)
2655 *aYOrigin = pFBInfo->yOrigin;
2656
2657 return S_OK;
2658}
2659
2660STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
2661 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
2662 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
2663{
2664 AutoCaller autoCaller(this);
2665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2666
2667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2668
2669 CHECK_CONSOLE_DRV(mpDrv);
2670
2671 /*
2672 * Do some rough checks for valid input.
2673 */
2674 ULONG width = aWidth;
2675 if (!width)
2676 width = mpDrv->IConnector.cx;
2677 ULONG height = aHeight;
2678 if (!height)
2679 height = mpDrv->IConnector.cy;
2680 ULONG bpp = aBitsPerPixel;
2681 if (!bpp)
2682 {
2683 uint32_t cBits = 0;
2684 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
2685 AssertRC(rc);
2686 bpp = cBits;
2687 }
2688 ULONG cMonitors;
2689 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
2690 if (cMonitors == 0 && aDisplay > 0)
2691 return E_INVALIDARG;
2692 if (aDisplay >= cMonitors)
2693 return E_INVALIDARG;
2694
2695 /*
2696 * sunlover 20070614: It is up to the guest to decide whether the hint is
2697 * valid. Therefore don't do any VRAM sanity checks here!
2698 */
2699
2700 /* Have to release the lock because the pfnRequestDisplayChange
2701 * will call EMT. */
2702 alock.release();
2703
2704 VMMDev *pVMMDev = mParent->getVMMDev();
2705 if (pVMMDev)
2706 {
2707 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2708 if (pVMMDevPort)
2709 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
2710 aDisplay, aOriginX, aOriginY,
2711 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
2712 }
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
2717{
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
2724 alock.release();
2725
2726 VMMDev *pVMMDev = mParent->getVMMDev();
2727 if (pVMMDev)
2728 {
2729 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2730 if (pVMMDevPort)
2731 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
2732 }
2733
2734#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2735 if (!enabled)
2736 {
2737 BOOL is3denabled = FALSE;
2738
2739 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2740
2741 VMMDev *vmmDev = mParent->getVMMDev();
2742 if (is3denabled && vmmDev)
2743 {
2744 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof (VBOXCRCMDCTL_HGCM));
2745 if (!pData)
2746 {
2747 AssertMsgFailed(("RTMemAlloc failed\n"));
2748 return VERR_NO_MEMORY;
2749 }
2750
2751 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2752 pData->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
2753
2754 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2755 pData->aParms[0].u.pointer.addr = NULL;
2756 pData->aParms[0].u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
2757
2758 int rc = crCtlSubmit(&pData->Hdr, sizeof (*pData), displayCrCmdFree, pData);
2759 if (!RT_SUCCESS(rc))
2760 {
2761 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
2762 RTMemFree(pData);
2763 }
2764 }
2765 }
2766#endif
2767 return S_OK;
2768}
2769
2770#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2771BOOL Display::displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pu8Data,
2772 uint32_t u32Width, uint32_t u32Height)
2773{
2774 BOOL is3denabled;
2775 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2776 if (is3denabled && pDisplay->mCrOglCallbacks.pfnHasData())
2777 {
2778 VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
2779 if (pVMMDev)
2780 {
2781 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)RTMemAlloc(sizeof (*pScreenshot));
2782 if (pScreenshot)
2783 {
2784 /* screen id or CRSCREEN_ALL to specify all enabled */
2785 pScreenshot->u32Screen = aScreenId;
2786 pScreenshot->u32Width = u32Width;
2787 pScreenshot->u32Height = u32Height;
2788 pScreenshot->u32Pitch = u32Width * 4;
2789 pScreenshot->pvBuffer = pu8Data;
2790 pScreenshot->pvContext = NULL;
2791 pScreenshot->pfnScreenshotBegin = NULL;
2792 pScreenshot->pfnScreenshotPerform = NULL;
2793 pScreenshot->pfnScreenshotEnd = NULL;
2794
2795 VBOXCRCMDCTL_HGCM data;
2796 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2797 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
2798
2799 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2800 data.aParms[0].u.pointer.addr = pScreenshot;
2801 data.aParms[0].u.pointer.size = sizeof (*pScreenshot);
2802
2803 int rc = pDisplay->crCtlSubmitSync(&data.Hdr, sizeof (data));
2804
2805 RTMemFree(pScreenshot);
2806
2807 if (RT_SUCCESS(rc))
2808 return TRUE;
2809 else
2810 {
2811 AssertMsgFailed(("failed to get screenshot data from crOgl %d\n", rc));
2812 /* fall back to the non-3d mechanism */
2813 }
2814 }
2815 }
2816 }
2817 return FALSE;
2818}
2819#endif
2820
2821int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData,
2822 uint32_t *pu32Width, uint32_t *pu32Height)
2823{
2824 int rc;
2825 pDisplay->vbvaLock();
2826 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2827 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2828 {
2829 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
2830 }
2831 else if (aScreenId < pDisplay->mcMonitors)
2832 {
2833 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2834
2835 uint32_t width = pFBInfo->w;
2836 uint32_t height = pFBInfo->h;
2837
2838 /* Allocate 32 bit per pixel bitmap. */
2839 size_t cbRequired = width * 4 * height;
2840
2841 if (cbRequired)
2842 {
2843 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
2844
2845 if (pu8Data == NULL)
2846 {
2847 rc = VERR_NO_MEMORY;
2848 }
2849 else
2850 {
2851 /* Copy guest VRAM to the allocated 32bpp buffer. */
2852 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2853 int32_t xSrc = 0;
2854 int32_t ySrc = 0;
2855 uint32_t u32SrcWidth = width;
2856 uint32_t u32SrcHeight = height;
2857 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2858 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2859
2860 uint8_t *pu8Dst = pu8Data;
2861 int32_t xDst = 0;
2862 int32_t yDst = 0;
2863 uint32_t u32DstWidth = u32SrcWidth;
2864 uint32_t u32DstHeight = u32SrcHeight;
2865 uint32_t u32DstLineSize = u32DstWidth * 4;
2866 uint32_t u32DstBitsPerPixel = 32;
2867
2868 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2869 width, height,
2870 pu8Src,
2871 xSrc, ySrc,
2872 u32SrcWidth, u32SrcHeight,
2873 u32SrcLineSize, u32SrcBitsPerPixel,
2874 pu8Dst,
2875 xDst, yDst,
2876 u32DstWidth, u32DstHeight,
2877 u32DstLineSize, u32DstBitsPerPixel);
2878 if (RT_SUCCESS(rc))
2879 {
2880 *ppu8Data = pu8Data;
2881 *pcbData = cbRequired;
2882 *pu32Width = width;
2883 *pu32Height = height;
2884 }
2885 else
2886 {
2887 RTMemFree(pu8Data);
2888
2889 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
2890 if ( rc == VERR_INVALID_STATE
2891 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2892 {
2893 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort,
2894 ppu8Data, pcbData, pu32Width, pu32Height);
2895 }
2896 }
2897 }
2898 }
2899 else
2900 {
2901 /* No image. */
2902 *ppu8Data = NULL;
2903 *pcbData = 0;
2904 *pu32Width = 0;
2905 *pu32Height = 0;
2906 rc = VINF_SUCCESS;
2907 }
2908 }
2909 else
2910 {
2911 rc = VERR_INVALID_PARAMETER;
2912 }
2913 pDisplay->vbvaUnlock();
2914 return rc;
2915}
2916
2917static int displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2918 BYTE *address, ULONG width, ULONG height)
2919{
2920 uint8_t *pu8Data = NULL;
2921 size_t cbData = 0;
2922 uint32_t cx = 0;
2923 uint32_t cy = 0;
2924 int vrc = VINF_SUCCESS;
2925
2926# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2927 if (Display::displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t*)address, width, height))
2928 return VINF_SUCCESS;
2929#endif
2930
2931 int cRetries = 5;
2932
2933 while (cRetries-- > 0)
2934 {
2935 /* Note! Not sure if the priority call is such a good idea here, but
2936 it would be nice to have an accurate screenshot for the bug
2937 report if the VM deadlocks. */
2938 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
2939 pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
2940 if (vrc != VERR_TRY_AGAIN)
2941 {
2942 break;
2943 }
2944
2945 RTThreadSleep(10);
2946 }
2947
2948 if (RT_SUCCESS(vrc) && pu8Data)
2949 {
2950 if (cx == width && cy == height)
2951 {
2952 /* No scaling required. */
2953 memcpy(address, pu8Data, cbData);
2954 }
2955 else
2956 {
2957 /* Scale. */
2958 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2959
2960 uint8_t *dst = address;
2961 uint8_t *src = pu8Data;
2962 int dstW = width;
2963 int dstH = height;
2964 int srcW = cx;
2965 int srcH = cy;
2966 int iDeltaLine = cx * 4;
2967
2968 BitmapScale32(dst,
2969 dstW, dstH,
2970 src,
2971 iDeltaLine,
2972 srcW, srcH);
2973 }
2974
2975 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2976 {
2977 /* This can be called from any thread. */
2978 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pu8Data);
2979 }
2980 else
2981 {
2982 RTMemFree(pu8Data);
2983 }
2984 }
2985
2986 return vrc;
2987}
2988
2989STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
2990{
2991 /// @todo (r=dmik) this function may take too long to complete if the VM
2992 // is doing something like saving state right now. Which, in case if it
2993 // is called on the GUI thread, will make it unresponsive. We should
2994 // check the machine state here (by enclosing the check and VMRequCall
2995 // within the Console lock to make it atomic).
2996
2997 LogRelFlowFunc(("address=%p, width=%d, height=%d\n",
2998 address, width, height));
2999
3000 CheckComArgNotNull(address);
3001 CheckComArgExpr(width, width != 0);
3002 CheckComArgExpr(height, height != 0);
3003
3004 /* Do not allow too large screenshots. This also filters out negative
3005 * values passed as either 'width' or 'height'.
3006 */
3007 CheckComArgExpr(width, width <= 32767);
3008 CheckComArgExpr(height, height <= 32767);
3009
3010 AutoCaller autoCaller(this);
3011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3012
3013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 if (!mpDrv)
3016 return E_FAIL;
3017
3018 Console::SafeVMPtr ptrVM(mParent);
3019 if (!ptrVM.isOk())
3020 return ptrVM.rc();
3021
3022 HRESULT rc = S_OK;
3023
3024 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
3025
3026 /* Release lock because other thread (EMT) is called and it may initiate a resize
3027 * which also needs lock.
3028 *
3029 * This method does not need the lock anymore.
3030 */
3031 alock.release();
3032
3033 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, address, width, height);
3034
3035 if (vrc == VERR_NOT_IMPLEMENTED)
3036 rc = setError(E_NOTIMPL,
3037 tr("This feature is not implemented"));
3038 else if (vrc == VERR_TRY_AGAIN)
3039 rc = setError(E_UNEXPECTED,
3040 tr("This feature is not available at this time"));
3041 else if (RT_FAILURE(vrc))
3042 rc = setError(VBOX_E_IPRT_ERROR,
3043 tr("Could not take a screenshot (%Rrc)"), vrc);
3044
3045 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3046 return rc;
3047}
3048
3049STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG height,
3050 ComSafeArrayOut(BYTE, aScreenData))
3051{
3052 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
3053
3054 CheckComArgOutSafeArrayPointerValid(aScreenData);
3055 CheckComArgExpr(width, width != 0);
3056 CheckComArgExpr(height, height != 0);
3057
3058 /* Do not allow too large screenshots. This also filters out negative
3059 * values passed as either 'width' or 'height'.
3060 */
3061 CheckComArgExpr(width, width <= 32767);
3062 CheckComArgExpr(height, height <= 32767);
3063
3064 AutoCaller autoCaller(this);
3065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3066
3067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 if (!mpDrv)
3070 return E_FAIL;
3071
3072 Console::SafeVMPtr ptrVM(mParent);
3073 if (!ptrVM.isOk())
3074 return ptrVM.rc();
3075
3076 HRESULT rc = S_OK;
3077
3078 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
3079
3080 /* Release lock because other thread (EMT) is called and it may initiate a resize
3081 * which also needs lock.
3082 *
3083 * This method does not need the lock anymore.
3084 */
3085 alock.release();
3086
3087 size_t cbData = width * 4 * height;
3088 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
3089
3090 if (!pu8Data)
3091 return E_OUTOFMEMORY;
3092
3093 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
3094
3095 if (RT_SUCCESS(vrc))
3096 {
3097 /* Convert pixels to format expected by the API caller: [0] R, [1] G, [2] B, [3] A. */
3098 uint8_t *pu8 = pu8Data;
3099 unsigned cPixels = width * height;
3100 while (cPixels)
3101 {
3102 uint8_t u8 = pu8[0];
3103 pu8[0] = pu8[2];
3104 pu8[2] = u8;
3105 pu8[3] = 0xff;
3106 cPixels--;
3107 pu8 += 4;
3108 }
3109
3110 com::SafeArray<BYTE> screenData(cbData);
3111 screenData.initFrom(pu8Data, cbData);
3112 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
3113 }
3114 else if (vrc == VERR_NOT_IMPLEMENTED)
3115 rc = setError(E_NOTIMPL,
3116 tr("This feature is not implemented"));
3117 else
3118 rc = setError(VBOX_E_IPRT_ERROR,
3119 tr("Could not take a screenshot (%Rrc)"), vrc);
3120
3121 RTMemFree(pu8Data);
3122
3123 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3124 return rc;
3125}
3126
3127STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULONG height,
3128 ComSafeArrayOut(BYTE, aScreenData))
3129{
3130 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
3131
3132 CheckComArgOutSafeArrayPointerValid(aScreenData);
3133 CheckComArgExpr(width, width != 0);
3134 CheckComArgExpr(height, height != 0);
3135
3136 /* Do not allow too large screenshots. This also filters out negative
3137 * values passed as either 'width' or 'height'.
3138 */
3139 CheckComArgExpr(width, width <= 32767);
3140 CheckComArgExpr(height, height <= 32767);
3141
3142 AutoCaller autoCaller(this);
3143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3144
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 CHECK_CONSOLE_DRV(mpDrv);
3148
3149 Console::SafeVMPtr ptrVM(mParent);
3150 if (!ptrVM.isOk())
3151 return ptrVM.rc();
3152
3153 HRESULT rc = S_OK;
3154
3155 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
3156
3157 /* Release lock because other thread (EMT) is called and it may initiate a resize
3158 * which also needs lock.
3159 *
3160 * This method does not need the lock anymore.
3161 */
3162 alock.release();
3163
3164 size_t cbData = width * 4 * height;
3165 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
3166
3167 if (!pu8Data)
3168 return E_OUTOFMEMORY;
3169
3170 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
3171
3172 if (RT_SUCCESS(vrc))
3173 {
3174 uint8_t *pu8PNG = NULL;
3175 uint32_t cbPNG = 0;
3176 uint32_t cxPNG = 0;
3177 uint32_t cyPNG = 0;
3178
3179 vrc = DisplayMakePNG(pu8Data, width, height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
3180 if (RT_SUCCESS(vrc))
3181 {
3182 com::SafeArray<BYTE> screenData(cbPNG);
3183 screenData.initFrom(pu8PNG, cbPNG);
3184 if (pu8PNG)
3185 RTMemFree(pu8PNG);
3186
3187 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
3188 }
3189 else
3190 {
3191 if (pu8PNG)
3192 RTMemFree(pu8PNG);
3193 rc = setError(VBOX_E_IPRT_ERROR,
3194 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
3195 }
3196 }
3197 else if (vrc == VERR_NOT_IMPLEMENTED)
3198 rc = setError(E_NOTIMPL,
3199 tr("This feature is not implemented"));
3200 else
3201 rc = setError(VBOX_E_IPRT_ERROR,
3202 tr("Could not take a screenshot (%Rrc)"), vrc);
3203
3204 RTMemFree(pu8Data);
3205
3206 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3207 return rc;
3208}
3209
3210int Display::VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
3211{
3212#ifdef VBOX_WITH_VPX
3213 com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
3214 for (unsigned i = 0; i < Screens.size(); i++)
3215 maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
3216 return VINF_SUCCESS;
3217#else
3218 return VERR_NOT_IMPLEMENTED;
3219#endif
3220}
3221
3222/**
3223 * Start video capturing. Does nothing if capturing is already active.
3224 */
3225int Display::VideoCaptureStart()
3226{
3227#ifdef VBOX_WITH_VPX
3228 if (VideoRecIsEnabled(mpVideoRecCtx))
3229 return VINF_SUCCESS;
3230
3231 int rc = VideoRecContextCreate(&mpVideoRecCtx, mcMonitors);
3232 if (RT_FAILURE(rc))
3233 {
3234 LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
3235 return rc;
3236 }
3237 ComPtr<IMachine> pMachine = mParent->machine();
3238 com::SafeArray<BOOL> screens;
3239 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
3240 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3241 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
3242 maVideoRecEnabled[i] = i < screens.size() && screens[i];
3243 ULONG ulWidth;
3244 hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
3245 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3246 ULONG ulHeight;
3247 hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
3248 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3249 ULONG ulRate;
3250 hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
3251 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3252 ULONG ulFPS;
3253 hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
3254 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3255 BSTR strFile;
3256 hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
3257 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3258 RTTIMESPEC ts;
3259 RTTimeNow(&ts);
3260 RTTIME time;
3261 RTTimeExplode(&time, &ts);
3262 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
3263 {
3264 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
3265 char *pszSuff = RTPathSuffix(pszAbsPath);
3266 if (pszSuff)
3267 pszSuff = RTStrDup(pszSuff);
3268 RTPathStripSuffix(pszAbsPath);
3269 if (!pszAbsPath)
3270 rc = VERR_INVALID_PARAMETER;
3271 if (!pszSuff)
3272 pszSuff = RTStrDup(".webm");
3273 char *pszName = NULL;
3274 if (RT_SUCCESS(rc))
3275 {
3276 if (mcMonitors > 1)
3277 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff);
3278 else
3279 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff);
3280 }
3281 if (RT_SUCCESS(rc))
3282 {
3283 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
3284 pszName, ulWidth, ulHeight, ulRate, ulFPS);
3285 if (rc == VERR_ALREADY_EXISTS)
3286 {
3287 RTStrFree(pszName);
3288 pszName = NULL;
3289
3290 if (mcMonitors > 1)
3291 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
3292 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
3293 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
3294 uScreen+1, pszSuff);
3295 else
3296 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
3297 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
3298 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
3299 pszSuff);
3300 if (RT_SUCCESS(rc))
3301 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
3302 pszName, ulWidth, ulHeight, ulRate, ulFPS);
3303 }
3304 }
3305
3306 if (RT_SUCCESS(rc))
3307 LogRel(("WebM/VP8 video recording screen #%u with %ux%u @ %u kbps, %u fps to '%s' enabled.\n",
3308 uScreen, ulWidth, ulHeight, ulRate, ulFPS, pszName));
3309 else
3310 LogRel(("Failed to initialize video recording context #%u (%Rrc)!\n", uScreen, rc));
3311 RTStrFree(pszName);
3312 RTStrFree(pszSuff);
3313 RTStrFree(pszAbsPath);
3314 }
3315 return rc;
3316#else
3317 return VERR_NOT_IMPLEMENTED;
3318#endif
3319}
3320
3321/**
3322 * Stop video capturing. Does nothing if video capturing is not active.
3323 */
3324void Display::VideoCaptureStop()
3325{
3326#ifdef VBOX_WITH_VPX
3327 if (VideoRecIsEnabled(mpVideoRecCtx))
3328 LogRel(("WebM/VP8 video recording stopped.\n"));
3329 VideoRecContextClose(mpVideoRecCtx);
3330 mpVideoRecCtx = NULL;
3331#endif
3332}
3333
3334int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
3335 ULONG x, ULONG y, ULONG width, ULONG height)
3336{
3337 int rc = VINF_SUCCESS;
3338 pDisplay->vbvaLock();
3339
3340 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
3341
3342 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3343 {
3344 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3345 {
3346 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
3347 }
3348 }
3349 else if (aScreenId < pDisplay->mcMonitors)
3350 {
3351 /* Copy the bitmap to the guest VRAM. */
3352 const uint8_t *pu8Src = address;
3353 int32_t xSrc = 0;
3354 int32_t ySrc = 0;
3355 uint32_t u32SrcWidth = width;
3356 uint32_t u32SrcHeight = height;
3357 uint32_t u32SrcLineSize = width * 4;
3358 uint32_t u32SrcBitsPerPixel = 32;
3359
3360 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
3361 int32_t xDst = x;
3362 int32_t yDst = y;
3363 uint32_t u32DstWidth = pFBInfo->w;
3364 uint32_t u32DstHeight = pFBInfo->h;
3365 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
3366 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
3367
3368 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3369 width, height,
3370 pu8Src,
3371 xSrc, ySrc,
3372 u32SrcWidth, u32SrcHeight,
3373 u32SrcLineSize, u32SrcBitsPerPixel,
3374 pu8Dst,
3375 xDst, yDst,
3376 u32DstWidth, u32DstHeight,
3377 u32DstLineSize, u32DstBitsPerPixel);
3378 if (RT_SUCCESS(rc))
3379 {
3380 if (!pFBInfo->pSourceBitmap.isNull())
3381 {
3382 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
3383 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
3384 */
3385 if ( pFBInfo->fDefaultFormat
3386 && !pFBInfo->fDisabled)
3387 {
3388 BYTE *pAddress = NULL;
3389 ULONG ulWidth = 0;
3390 ULONG ulHeight = 0;
3391 ULONG ulBitsPerPixel = 0;
3392 ULONG ulBytesPerLine = 0;
3393 ULONG ulPixelFormat = 0;
3394
3395 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3396 &ulWidth,
3397 &ulHeight,
3398 &ulBitsPerPixel,
3399 &ulBytesPerLine,
3400 &ulPixelFormat);
3401 if (SUCCEEDED(hrc))
3402 {
3403 pu8Src = pFBInfo->pu8FramebufferVRAM;
3404 xSrc = x;
3405 ySrc = y;
3406 u32SrcWidth = pFBInfo->w;
3407 u32SrcHeight = pFBInfo->h;
3408 u32SrcLineSize = pFBInfo->u32LineSize;
3409 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3410
3411 /* Default format is 32 bpp. */
3412 pu8Dst = pAddress;
3413 xDst = xSrc;
3414 yDst = ySrc;
3415 u32DstWidth = u32SrcWidth;
3416 u32DstHeight = u32SrcHeight;
3417 u32DstLineSize = u32DstWidth * 4;
3418 u32DstBitsPerPixel = 32;
3419
3420 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3421 width, height,
3422 pu8Src,
3423 xSrc, ySrc,
3424 u32SrcWidth, u32SrcHeight,
3425 u32SrcLineSize, u32SrcBitsPerPixel,
3426 pu8Dst,
3427 xDst, yDst,
3428 u32DstWidth, u32DstHeight,
3429 u32DstLineSize, u32DstBitsPerPixel);
3430 }
3431 }
3432 }
3433
3434 pDisplay->handleDisplayUpdate(aScreenId, x, y, width, height);
3435 }
3436 }
3437 else
3438 {
3439 rc = VERR_INVALID_PARAMETER;
3440 }
3441
3442 if ( RT_SUCCESS(rc)
3443 && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
3444 pDisplay->mParent->consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
3445
3446 pDisplay->vbvaUnlock();
3447 return rc;
3448}
3449
3450STDMETHODIMP Display::DrawToScreen(ULONG aScreenId, BYTE *address,
3451 ULONG x, ULONG y, ULONG width, ULONG height)
3452{
3453 /// @todo (r=dmik) this function may take too long to complete if the VM
3454 // is doing something like saving state right now. Which, in case if it
3455 // is called on the GUI thread, will make it unresponsive. We should
3456 // check the machine state here (by enclosing the check and VMRequCall
3457 // within the Console lock to make it atomic).
3458
3459 LogRelFlowFunc(("address=%p, x=%d, y=%d, width=%d, height=%d\n",
3460 (void *)address, x, y, width, height));
3461
3462 CheckComArgNotNull(address);
3463 CheckComArgExpr(width, width != 0);
3464 CheckComArgExpr(height, height != 0);
3465
3466 AutoCaller autoCaller(this);
3467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3468
3469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3470
3471 CHECK_CONSOLE_DRV(mpDrv);
3472
3473 Console::SafeVMPtr ptrVM(mParent);
3474 if (!ptrVM.isOk())
3475 return ptrVM.rc();
3476
3477 /* Release lock because the call scheduled on EMT may also try to take it. */
3478 alock.release();
3479
3480 /*
3481 * Again we're lazy and make the graphics device do all the
3482 * dirty conversion work.
3483 */
3484 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
3485 this, aScreenId, address, x, y, width, height);
3486
3487 /*
3488 * If the function returns not supported, we'll have to do all the
3489 * work ourselves using the framebuffer.
3490 */
3491 HRESULT rc = S_OK;
3492 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
3493 {
3494 /** @todo implement generic fallback for screen blitting. */
3495 rc = E_NOTIMPL;
3496 }
3497 else if (RT_FAILURE(rcVBox))
3498 rc = setError(VBOX_E_IPRT_ERROR,
3499 tr("Could not draw to the screen (%Rrc)"), rcVBox);
3500//@todo
3501// else
3502// {
3503// /* All ok. Redraw the screen. */
3504// handleDisplayUpdate (x, y, width, height);
3505// }
3506
3507 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3508 return rc;
3509}
3510
3511void Display::InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
3512{
3513 pDisplay->vbvaLock();
3514 unsigned uScreenId;
3515 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
3516 {
3517 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3518
3519 if ( !pFBInfo->fVBVAEnabled
3520 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3521 {
3522 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort);
3523 }
3524 else
3525 {
3526 if ( !pFBInfo->fDisabled
3527 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3528 {
3529 /* Render complete VRAM screen to the framebuffer.
3530 * When framebuffer uses VRAM directly, just notify it to update.
3531 */
3532 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
3533 {
3534 BYTE *pAddress = NULL;
3535 ULONG ulWidth = 0;
3536 ULONG ulHeight = 0;
3537 ULONG ulBitsPerPixel = 0;
3538 ULONG ulBytesPerLine = 0;
3539 ULONG ulPixelFormat = 0;
3540
3541 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3542 &ulWidth,
3543 &ulHeight,
3544 &ulBitsPerPixel,
3545 &ulBytesPerLine,
3546 &ulPixelFormat);
3547 if (SUCCEEDED(hrc))
3548 {
3549 uint32_t width = pFBInfo->w;
3550 uint32_t height = pFBInfo->h;
3551
3552 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3553 int32_t xSrc = 0;
3554 int32_t ySrc = 0;
3555 uint32_t u32SrcWidth = pFBInfo->w;
3556 uint32_t u32SrcHeight = pFBInfo->h;
3557 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3558 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3559
3560 /* Default format is 32 bpp. */
3561 uint8_t *pu8Dst = pAddress;
3562 int32_t xDst = xSrc;
3563 int32_t yDst = ySrc;
3564 uint32_t u32DstWidth = u32SrcWidth;
3565 uint32_t u32DstHeight = u32SrcHeight;
3566 uint32_t u32DstLineSize = u32DstWidth * 4;
3567 uint32_t u32DstBitsPerPixel = 32;
3568
3569 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
3570 * implies resize of Framebuffer is in progress and
3571 * copyrect should not be called.
3572 */
3573 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
3574 {
3575
3576 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3577 width, height,
3578 pu8Src,
3579 xSrc, ySrc,
3580 u32SrcWidth, u32SrcHeight,
3581 u32SrcLineSize, u32SrcBitsPerPixel,
3582 pu8Dst,
3583 xDst, yDst,
3584 u32DstWidth, u32DstHeight,
3585 u32DstLineSize, u32DstBitsPerPixel);
3586 }
3587 }
3588 }
3589
3590 pDisplay->handleDisplayUpdate (uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
3591 }
3592 }
3593 if (!fUpdateAll)
3594 break;
3595 }
3596 pDisplay->vbvaUnlock();
3597}
3598
3599/**
3600 * Does a full invalidation of the VM display and instructs the VM
3601 * to update it immediately.
3602 *
3603 * @returns COM status code
3604 */
3605STDMETHODIMP Display::InvalidateAndUpdate()
3606{
3607 LogRelFlowFunc(("\n"));
3608
3609 AutoCaller autoCaller(this);
3610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3611
3612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3613
3614 CHECK_CONSOLE_DRV(mpDrv);
3615
3616 Console::SafeVMPtr ptrVM(mParent);
3617 if (!ptrVM.isOk())
3618 return ptrVM.rc();
3619
3620 HRESULT rc = S_OK;
3621
3622 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
3623
3624 /* Have to release the lock when calling EMT. */
3625 alock.release();
3626
3627 /* pdm.h says that this has to be called from the EMT thread */
3628 int rcVBox = VMR3ReqCallVoidWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
3629 3, this, 0, true);
3630 alock.acquire();
3631
3632 if (RT_FAILURE(rcVBox))
3633 rc = setError(VBOX_E_IPRT_ERROR,
3634 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
3635
3636 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3637 return rc;
3638}
3639
3640STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
3641{
3642#ifdef VBOX_WITH_VIDEOHWACCEL
3643 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
3644 return S_OK;
3645#else
3646 return E_NOTIMPL;
3647#endif
3648}
3649
3650STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3651{
3652#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3653
3654 if (mcMonitors <= aScreenId)
3655 {
3656 AssertMsgFailed(("invalid screen id\n"));
3657 return E_INVALIDARG;
3658 }
3659
3660 BOOL is3denabled;
3661 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3662
3663 if (is3denabled)
3664 {
3665 int rc = crViewportNotify(aScreenId, x, y, width, height);
3666 if (RT_FAILURE(rc))
3667 {
3668 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
3669 pFb->pendingViewportInfo.fPending = true;
3670 pFb->pendingViewportInfo.x = x;
3671 pFb->pendingViewportInfo.y = y;
3672 pFb->pendingViewportInfo.width = width;
3673 pFb->pendingViewportInfo.height = height;
3674 }
3675 }
3676#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
3677 return S_OK;
3678}
3679
3680STDMETHODIMP Display::QuerySourceBitmap(ULONG aScreenId,
3681 IDisplaySourceBitmap **aDisplaySourceBitmap)
3682{
3683 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
3684
3685 AutoCaller autoCaller(this);
3686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3687
3688 Console::SafeVMPtr ptrVM(mParent);
3689 if (!ptrVM.isOk())
3690 return ptrVM.rc();
3691
3692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3693
3694 if (aScreenId >= mcMonitors)
3695 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
3696 aScreenId, mcMonitors);
3697
3698 HRESULT hr = querySourceBitmap(aScreenId, aDisplaySourceBitmap);
3699
3700 alock.release();
3701
3702 LogRelFlowFunc(("%Rhrc\n", hr));
3703 return hr;
3704}
3705
3706// private methods
3707/////////////////////////////////////////////////////////////////////////////
3708
3709HRESULT Display::querySourceBitmap(ULONG aScreenId,
3710 IDisplaySourceBitmap **ppDisplaySourceBitmap)
3711{
3712 HRESULT hr = S_OK;
3713
3714 if (!mfSourceBitmapEnabled)
3715 {
3716 *ppDisplaySourceBitmap = NULL;
3717 return E_FAIL;
3718 }
3719
3720 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
3721 if (pFBInfo->pSourceBitmap.isNull())
3722 {
3723 /* Create a new object. */
3724 ComObjPtr<DisplaySourceBitmap> obj;
3725 hr = obj.createObject();
3726 if (SUCCEEDED(hr))
3727 {
3728 hr = obj->init(this, aScreenId, pFBInfo);
3729 }
3730
3731 if (SUCCEEDED(hr))
3732 {
3733 pFBInfo->pSourceBitmap = obj;
3734
3735 /* Whether VRAM must be copied to the internal buffer. */
3736 pFBInfo->fDefaultFormat = !obj->usesVRAM();
3737
3738 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3739 {
3740 /* Start buffer updates. */
3741 BYTE *pAddress = NULL;
3742 ULONG ulWidth = 0;
3743 ULONG ulHeight = 0;
3744 ULONG ulBitsPerPixel = 0;
3745 ULONG ulBytesPerLine = 0;
3746 ULONG ulPixelFormat = 0;
3747
3748 obj->QueryBitmapInfo(&pAddress,
3749 &ulWidth,
3750 &ulHeight,
3751 &ulBitsPerPixel,
3752 &ulBytesPerLine,
3753 &ulPixelFormat);
3754
3755 mpDrv->IConnector.pu8Data = pAddress;
3756 mpDrv->IConnector.cbScanline = ulBytesPerLine;
3757 mpDrv->IConnector.cBits = ulBitsPerPixel;
3758 mpDrv->IConnector.cx = ulWidth;
3759 mpDrv->IConnector.cy = ulHeight;
3760
3761 if (pFBInfo->fDefaultFormat)
3762 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
3763 }
3764
3765 if (pFBInfo->fDefaultFormat)
3766 {
3767 /* @todo make sure that the bitmap contains the latest image? */
3768 }
3769 }
3770 }
3771
3772 if (SUCCEEDED(hr))
3773 {
3774 pFBInfo->pSourceBitmap->AddRef();
3775 *ppDisplaySourceBitmap = pFBInfo->pSourceBitmap;
3776 }
3777
3778 return hr;
3779}
3780
3781/**
3782 * Helper to update the display information from the framebuffer.
3783 *
3784 * @thread EMT
3785 */
3786int Display::updateDisplayData(void)
3787{
3788 LogRelFlowFunc(("\n"));
3789
3790 /* the driver might not have been constructed yet */
3791 if (!mpDrv)
3792 return VINF_SUCCESS;
3793
3794#ifdef VBOX_STRICT
3795 /*
3796 * Sanity check. Note that this method may be called on EMT after Console
3797 * has started the power down procedure (but before our #drvDestruct() is
3798 * called, in which case pVM will already be NULL but mpDrv will not). Since
3799 * we don't really need pVM to proceed, we avoid this check in the release
3800 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
3801 * time-critical method.
3802 */
3803 Console::SafeVMPtrQuiet ptrVM(mParent);
3804 if (ptrVM.isOk())
3805 {
3806 PVM pVM = VMR3GetVM(ptrVM.rawUVM());
3807 Assert(VM_IS_EMT(pVM));
3808 }
3809#endif
3810
3811 /* The method is only relevant to the primary framebuffer. */
3812 IFramebuffer *pFramebuffer = maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer;
3813
3814 if (pFramebuffer)
3815 {
3816 HRESULT rc;
3817 BYTE *address = 0;
3818 rc = pFramebuffer->COMGETTER(Address) (&address);
3819 AssertComRC (rc);
3820 ULONG bytesPerLine = 0;
3821 rc = pFramebuffer->COMGETTER(BytesPerLine) (&bytesPerLine);
3822 AssertComRC (rc);
3823 ULONG bitsPerPixel = 0;
3824 rc = pFramebuffer->COMGETTER(BitsPerPixel) (&bitsPerPixel);
3825 AssertComRC (rc);
3826 ULONG width = 0;
3827 rc = pFramebuffer->COMGETTER(Width) (&width);
3828 AssertComRC (rc);
3829 ULONG height = 0;
3830 rc = pFramebuffer->COMGETTER(Height) (&height);
3831 AssertComRC (rc);
3832
3833 if ( (width != mLastWidth && mLastWidth != 0)
3834 || (height != mLastHeight && mLastHeight != 0))
3835 {
3836 LogRel(("updateDisplayData: size mismatch w %d(%d) h %d(%d)\n",
3837 width, mLastWidth, height, mLastHeight));
3838 return VERR_INVALID_STATE;
3839 }
3840
3841 mpDrv->IConnector.pu8Data = (uint8_t *) address;
3842 mpDrv->IConnector.cbScanline = bytesPerLine;
3843 mpDrv->IConnector.cBits = bitsPerPixel;
3844 mpDrv->IConnector.cx = width;
3845 mpDrv->IConnector.cy = height;
3846 }
3847 else
3848 {
3849 /* black hole */
3850 mpDrv->IConnector.pu8Data = NULL;
3851 mpDrv->IConnector.cbScanline = 0;
3852 mpDrv->IConnector.cBits = 0;
3853 mpDrv->IConnector.cx = 0;
3854 mpDrv->IConnector.cy = 0;
3855 }
3856 LogRelFlowFunc(("leave\n"));
3857 return VINF_SUCCESS;
3858}
3859
3860#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3861int Display::crViewportNotify(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3862{
3863 VMMDev *pVMMDev = mParent->getVMMDev();
3864 if (!pVMMDev)
3865 return VERR_INVALID_STATE;
3866
3867 size_t cbData = RT_UOFFSETOF(VBOXCRCMDCTL_HGCM, aParms[5]);
3868 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)alloca(cbData);
3869
3870 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3871 pData->Hdr.u32Function = SHCRGL_HOST_FN_VIEWPORT_CHANGED;
3872
3873 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
3874 pData->aParms[0].u.uint32 = aScreenId;
3875
3876 pData->aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
3877 pData->aParms[1].u.uint32 = x;
3878
3879 pData->aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
3880 pData->aParms[2].u.uint32 = y;
3881
3882 pData->aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
3883 pData->aParms[3].u.uint32 = width;
3884
3885 pData->aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
3886 pData->aParms[4].u.uint32 = height;
3887
3888 return crCtlSubmitSyncIfHasDataForScreen(aScreenId, &pData->Hdr, cbData);
3889}
3890#endif
3891
3892#ifdef VBOX_WITH_CRHGSMI
3893void Display::setupCrHgsmiData(void)
3894{
3895 VMMDev *pVMMDev = mParent->getVMMDev();
3896 Assert(pVMMDev);
3897 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3898 AssertRC(rc);
3899
3900 if (pVMMDev)
3901 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3902 else
3903 rc = VERR_GENERAL_FAILURE;
3904
3905 if (RT_SUCCESS(rc))
3906 {
3907 Assert(mhCrOglSvc);
3908 /* setup command completion callback */
3909 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
3910 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
3911 Completion.Hdr.cbCmd = sizeof (Completion);
3912 Completion.hCompletion = mpDrv->pVBVACallbacks;
3913 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3914
3915 VBOXHGCMSVCPARM parm;
3916 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3917 parm.u.pointer.addr = &Completion;
3918 parm.u.pointer.size = 0;
3919
3920 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3921 if (RT_SUCCESS(rc))
3922 mCrOglCallbacks = Completion.MainInterface;
3923 else
3924 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed rc %d", rc));
3925 }
3926
3927 if (RT_FAILURE(rc))
3928 mhCrOglSvc = NULL;
3929
3930 RTCritSectRwLeaveExcl(&mCrOglLock);
3931}
3932
3933void Display::destructCrHgsmiData(void)
3934{
3935 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3936 AssertRC(rc);
3937 mhCrOglSvc = NULL;
3938 RTCritSectRwLeaveExcl(&mCrOglLock);
3939}
3940#endif
3941
3942/**
3943 * Changes the current frame buffer. Called on EMT to avoid both
3944 * race conditions and excessive locking.
3945 *
3946 * @note locks this object for writing
3947 * @thread EMT
3948 */
3949/* static */
3950DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
3951 unsigned uScreenId)
3952{
3953 LogRelFlowFunc(("uScreenId = %d\n", uScreenId));
3954
3955 AssertReturn(that, VERR_INVALID_PARAMETER);
3956 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
3957
3958 AutoCaller autoCaller(that);
3959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3960
3961 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
3962
3963 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
3964 pDisplayFBInfo->pFramebuffer = aFB;
3965
3966 that->mParent->consoleVRDPServer()->SendResize ();
3967
3968 /* The driver might not have been constructed yet */
3969 if (that->mpDrv)
3970 {
3971 /* Setup the new framebuffer, the resize will lead to an updateDisplayData call. */
3972 DISPLAYFBINFO *pFBInfo = &that->maFramebuffers[uScreenId];
3973
3974#if defined(VBOX_WITH_CROGL)
3975 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
3976 {
3977 BOOL is3denabled;
3978 that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3979
3980 if (is3denabled)
3981 {
3982 alock.release();
3983 }
3984 }
3985#endif
3986
3987 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
3988 {
3989 /* This display in VBVA mode. Resize it to the last guest resolution,
3990 * if it has been reported.
3991 */
3992 that->handleDisplayResize(uScreenId, pFBInfo->u16BitsPerPixel,
3993 pFBInfo->pu8FramebufferVRAM,
3994 pFBInfo->u32LineSize,
3995 pFBInfo->w,
3996 pFBInfo->h,
3997 pFBInfo->flags);
3998 }
3999 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4000 {
4001 /* VGA device mode, only for the primary screen. */
4002 that->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, that->mLastBitsPerPixel,
4003 that->mLastAddress,
4004 that->mLastBytesPerLine,
4005 that->mLastWidth,
4006 that->mLastHeight,
4007 that->mLastFlags);
4008 }
4009 }
4010
4011 LogRelFlowFunc(("leave\n"));
4012 return VINF_SUCCESS;
4013}
4014
4015/**
4016 * Handle display resize event issued by the VGA device for the primary screen.
4017 *
4018 * @see PDMIDISPLAYCONNECTOR::pfnResize
4019 */
4020DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
4021 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
4022{
4023 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4024
4025 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
4026 bpp, pvVRAM, cbLine, cx, cy));
4027
4028 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
4029}
4030
4031/**
4032 * Handle display update.
4033 *
4034 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
4035 */
4036DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
4037 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4038{
4039 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4040
4041#ifdef DEBUG_sunlover
4042 LogFlowFunc(("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
4043 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
4044#endif /* DEBUG_sunlover */
4045
4046 /* This call does update regardless of VBVA status.
4047 * But in VBVA mode this is called only as result of
4048 * pfnUpdateDisplayAll in the VGA device.
4049 */
4050
4051 pDrv->pDisplay->handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
4052}
4053
4054/**
4055 * Periodic display refresh callback.
4056 *
4057 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
4058 * @thread EMT
4059 */
4060DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
4061{
4062 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4063
4064#ifdef DEBUG_sunlover
4065 STAM_PROFILE_START(&g_StatDisplayRefresh, a);
4066#endif /* DEBUG_sunlover */
4067
4068#ifdef DEBUG_sunlover_2
4069 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
4070 pDrv->pDisplay->mfVideoAccelEnabled));
4071#endif /* DEBUG_sunlover_2 */
4072
4073 Display *pDisplay = pDrv->pDisplay;
4074 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
4075 unsigned uScreenId;
4076
4077 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
4078 {
4079 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
4080
4081 /* Check the resize status. The status can be checked normally because
4082 * the status affects only the EMT.
4083 */
4084 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
4085
4086 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
4087 {
4088 LogRelFlowFunc(("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
4089 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
4090 /* The framebuffer was resized and display data need to be updated. */
4091 pDisplay->handleResizeCompletedEMT(uScreenId, FALSE);
4092 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
4093 {
4094 /* The resize status could be not Void here because a pending resize is issued. */
4095 continue;
4096 }
4097
4098 /* Repaint the display because VM continued to run during the framebuffer resize. */
4099 pDisplay->InvalidateAndUpdateEMT(pDisplay, uScreenId, false);
4100
4101 /* Continue with normal processing because the status here is ResizeStatus_Void. */
4102 }
4103 else if (u32ResizeStatus == ResizeStatus_InProgress)
4104 {
4105 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
4106 LogRelFlowFunc(("ResizeStatus_InProcess\n"));
4107 fNoUpdate = true;
4108 continue;
4109 }
4110 }
4111
4112 if (!fNoUpdate)
4113 {
4114 int rc = pDisplay->videoAccelRefreshProcess();
4115 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
4116 {
4117 if (rc == VWRN_INVALID_STATE)
4118 {
4119 /* No VBVA do a display update. */
4120 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
4121 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4122 {
4123 pDisplay->vbvaLock();
4124 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
4125 pDisplay->vbvaUnlock();
4126 }
4127 }
4128
4129 /* Inform the VRDP server that the current display update sequence is
4130 * completed. At this moment the framebuffer memory contains a definite
4131 * image, that is synchronized with the orders already sent to VRDP client.
4132 * The server can now process redraw requests from clients or initial
4133 * fullscreen updates for new clients.
4134 */
4135 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
4136 {
4137 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
4138
4139 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4140 {
4141 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
4142 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
4143 }
4144 }
4145 }
4146 }
4147
4148#ifdef VBOX_WITH_VPX
4149 if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
4150 {
4151 do {
4152# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4153 BOOL is3denabled;
4154 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
4155 if (is3denabled)
4156 {
4157 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
4158 {
4159 if (pDisplay->mCrOglCallbacks.pfnHasData())
4160 {
4161 /* submit */
4162 VBOXCRCMDCTL_HGCM *pData = &pDisplay->mCrOglScreenshotCtl;
4163
4164 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
4165 pData->Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
4166
4167 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
4168 pData->aParms[0].u.pointer.addr = &pDisplay->mCrOglScreenshotData;
4169 pData->aParms[0].u.pointer.size = sizeof (pDisplay->mCrOglScreenshotData);
4170 int rc = pDisplay->crCtlSubmit(&pData->Hdr, sizeof (*pData), displayCrCmdFree, pData);
4171 if (!RT_SUCCESS(rc))
4172 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
4173 }
4174
4175 /* no 3D data available, or error has occured,
4176 * go the straight way */
4177 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4178 }
4179 else
4180 {
4181 /* record request is still in progress, don't do anything */
4182 break;
4183 }
4184 }
4185# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
4186
4187 uint64_t u64Now = RTTimeProgramMilliTS();
4188 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
4189 {
4190 if (!pDisplay->maVideoRecEnabled[uScreenId])
4191 continue;
4192
4193 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
4194
4195 if ( !pFBInfo->pFramebuffer.isNull()
4196 && !pFBInfo->fDisabled
4197 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4198 {
4199 int rc = VERR_NOT_SUPPORTED;
4200 if ( pFBInfo->fVBVAEnabled
4201 && pFBInfo->pu8FramebufferVRAM)
4202 {
4203 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
4204 FramebufferPixelFormat_FOURCC_RGB,
4205 pFBInfo->u16BitsPerPixel,
4206 pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
4207 pFBInfo->pu8FramebufferVRAM, u64Now);
4208 }
4209 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && pDrv->IConnector.pu8Data)
4210 {
4211 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
4212 FramebufferPixelFormat_FOURCC_RGB,
4213 pDrv->IConnector.cBits,
4214 pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
4215 pDrv->IConnector.cy, pDrv->IConnector.pu8Data, u64Now);
4216 }
4217 if (rc == VINF_TRY_AGAIN)
4218 break;
4219 }
4220 }
4221 } while (0);
4222 }
4223#endif /* VBOX_WITH_VPX */
4224
4225#ifdef DEBUG_sunlover
4226 STAM_PROFILE_STOP(&g_StatDisplayRefresh, a);
4227#endif /* DEBUG_sunlover */
4228#ifdef DEBUG_sunlover_2
4229 LogFlowFunc(("leave\n"));
4230#endif /* DEBUG_sunlover_2 */
4231}
4232
4233/**
4234 * Reset notification
4235 *
4236 * @see PDMIDISPLAYCONNECTOR::pfnReset
4237 */
4238DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
4239{
4240 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4241
4242 LogRelFlowFunc(("\n"));
4243
4244 /* Disable VBVA mode. */
4245 pDrv->pDisplay->VideoAccelEnable (false, NULL);
4246}
4247
4248/**
4249 * LFBModeChange notification
4250 *
4251 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
4252 */
4253DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
4254{
4255 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4256
4257 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
4258
4259 NOREF(fEnabled);
4260
4261 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
4262 /* The LFBModeChange function is called under DevVGA lock. Postpone disabling VBVA, do it in the refresh timer. */
4263 ASMAtomicWriteU32(&pDrv->pDisplay->mfu32PendingVideoAccelDisable, true);
4264}
4265
4266/**
4267 * Adapter information change notification.
4268 *
4269 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
4270 */
4271DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
4272 uint32_t u32VRAMSize)
4273{
4274 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4275
4276 if (pvVRAM == NULL)
4277 {
4278 unsigned i;
4279 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
4280 {
4281 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
4282
4283 pFBInfo->u32Offset = 0;
4284 pFBInfo->u32MaxFramebufferSize = 0;
4285 pFBInfo->u32InformationSize = 0;
4286 }
4287 }
4288#ifndef VBOX_WITH_HGSMI
4289 else
4290 {
4291 uint8_t *pu8 = (uint8_t *)pvVRAM;
4292 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
4293
4294 // @todo
4295 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
4296
4297 VBOXVIDEOINFOHDR *pHdr;
4298
4299 for (;;)
4300 {
4301 pHdr = (VBOXVIDEOINFOHDR *)pu8;
4302 pu8 += sizeof (VBOXVIDEOINFOHDR);
4303
4304 if (pu8 >= pu8End)
4305 {
4306 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
4307 break;
4308 }
4309
4310 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
4311 {
4312 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
4313 {
4314 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
4315 break;
4316 }
4317
4318 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
4319
4320 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
4321 {
4322 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
4323 break;
4324 }
4325
4326 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
4327
4328 pFBInfo->u32Offset = pDisplay->u32Offset;
4329 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
4330 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
4331
4332 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index,
4333 pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
4334 }
4335 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
4336 {
4337 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
4338 {
4339 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
4340 break;
4341 }
4342
4343 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
4344
4345 switch (pConf32->u32Index)
4346 {
4347 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
4348 {
4349 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
4350 } break;
4351
4352 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
4353 {
4354 /* @todo make configurable. */
4355 pConf32->u32Value = _1M;
4356 } break;
4357
4358 default:
4359 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
4360 }
4361 }
4362 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
4363 {
4364 if (pHdr->u16Length != 0)
4365 {
4366 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4367 break;
4368 }
4369
4370 break;
4371 }
4372 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP)
4373 {
4374 /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo. cpp pushing this to us? */
4375 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
4376 }
4377
4378 pu8 += pHdr->u16Length;
4379 }
4380 }
4381#endif /* !VBOX_WITH_HGSMI */
4382}
4383
4384/**
4385 * Display information change notification.
4386 *
4387 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
4388 */
4389DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
4390{
4391 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4392
4393 if (uScreenId >= pDrv->pDisplay->mcMonitors)
4394 {
4395 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
4396 return;
4397 }
4398
4399 /* Get the display information structure. */
4400 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
4401
4402 uint8_t *pu8 = (uint8_t *)pvVRAM;
4403 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
4404
4405 // @todo
4406 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
4407
4408 VBOXVIDEOINFOHDR *pHdr;
4409
4410 for (;;)
4411 {
4412 pHdr = (VBOXVIDEOINFOHDR *)pu8;
4413 pu8 += sizeof (VBOXVIDEOINFOHDR);
4414
4415 if (pu8 >= pu8End)
4416 {
4417 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
4418 break;
4419 }
4420
4421 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
4422 {
4423 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
4424 {
4425 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
4426 break;
4427 }
4428
4429 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
4430
4431 pFBInfo->xOrigin = pScreen->xOrigin;
4432 pFBInfo->yOrigin = pScreen->yOrigin;
4433
4434 pFBInfo->w = pScreen->u16Width;
4435 pFBInfo->h = pScreen->u16Height;
4436
4437 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
4438 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width,
4439 pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
4440
4441 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
4442 {
4443 /* Primary screen resize is eeeeeeeee by the VGA device. */
4444 if (pFBInfo->fDisabled)
4445 {
4446 pFBInfo->fDisabled = false;
4447 fireGuestMonitorChangedEvent(pDrv->pDisplay->mParent->getEventSource(),
4448 GuestMonitorChangedEventType_Enabled,
4449 uScreenId,
4450 pFBInfo->xOrigin, pFBInfo->yOrigin,
4451 pFBInfo->w, pFBInfo->h);
4452 }
4453
4454 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel,
4455 (uint8_t *)pvVRAM + pFBInfo->u32Offset,
4456 pScreen->u32LineSize,
4457 pScreen->u16Width, pScreen->u16Height,
4458 VBVA_SCREEN_F_ACTIVE);
4459 }
4460 }
4461 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
4462 {
4463 if (pHdr->u16Length != 0)
4464 {
4465 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4466 break;
4467 }
4468
4469 break;
4470 }
4471 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
4472 {
4473 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
4474 {
4475 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
4476 break;
4477 }
4478
4479 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
4480
4481 pFBInfo->pHostEvents = pHostEvents;
4482
4483 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
4484 pHostEvents));
4485 }
4486 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
4487 {
4488 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
4489 {
4490 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
4491 break;
4492 }
4493
4494 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
4495 pu8 += pLink->i32Offset;
4496 }
4497 else
4498 {
4499 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
4500 }
4501
4502 pu8 += pHdr->u16Length;
4503 }
4504}
4505
4506#ifdef VBOX_WITH_VIDEOHWACCEL
4507
4508#ifndef S_FALSE
4509# define S_FALSE ((HRESULT)1L)
4510#endif
4511
4512int Display::handleVHWACommandProcess(PVBOXVHWACMD pCommand)
4513{
4514 unsigned id = (unsigned)pCommand->iDisplay;
4515 int rc = VINF_SUCCESS;
4516 if (id >= mcMonitors)
4517 return VERR_INVALID_PARAMETER;
4518
4519 ComPtr<IFramebuffer> pFramebuffer;
4520 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4521 pFramebuffer = maFramebuffers[id].pFramebuffer;
4522 arlock.release();
4523
4524 if (pFramebuffer == NULL)
4525 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
4526
4527 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
4528 if (hr == S_FALSE)
4529 return VINF_SUCCESS;
4530 else if (SUCCEEDED(hr))
4531 return VINF_CALLBACK_RETURN;
4532 else if (hr == E_ACCESSDENIED)
4533 return VERR_INVALID_STATE; /* notify we can not handle request atm */
4534 else if (hr == E_NOTIMPL)
4535 return VERR_NOT_IMPLEMENTED;
4536 return VERR_GENERAL_FAILURE;
4537}
4538
4539DECLCALLBACK(int) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
4540{
4541 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4542
4543 return pDrv->pDisplay->handleVHWACommandProcess(pCommand);
4544}
4545#endif
4546
4547#ifdef VBOX_WITH_CRHGSMI
4548void Display::handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4549{
4550 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks,
4551 (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
4552}
4553
4554void Display::handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4555{
4556 PVBOXVDMACMD_CHROMIUM_CTL pCtl = (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr;
4557 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, pCtl, result);
4558}
4559
4560void Display::handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
4561{
4562 int rc = VERR_NOT_SUPPORTED;
4563 VBOXHGCMSVCPARM parm;
4564 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4565 parm.u.pointer.addr = pCmd;
4566 parm.u.pointer.size = cbCmd;
4567
4568 if (mhCrOglSvc)
4569 {
4570 VMMDev *pVMMDev = mParent->getVMMDev();
4571 if (pVMMDev)
4572 {
4573 /* no completion callback is specified with this call,
4574 * the CrOgl code will complete the CrHgsmi command once it processes it */
4575 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
4576 AssertRC(rc);
4577 if (RT_SUCCESS(rc))
4578 return;
4579 }
4580 else
4581 rc = VERR_INVALID_STATE;
4582 }
4583
4584 /* we are here because something went wrong with command processing, complete it */
4585 handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
4586}
4587
4588void Display::handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
4589{
4590 int rc = VERR_NOT_SUPPORTED;
4591 VBOXHGCMSVCPARM parm;
4592 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4593 parm.u.pointer.addr = pCtl;
4594 parm.u.pointer.size = cbCtl;
4595
4596 if (mhCrOglSvc)
4597 {
4598 VMMDev *pVMMDev = mParent->getVMMDev();
4599 if (pVMMDev)
4600 {
4601 bool fCheckPendingViewport = (pCtl->enmType == VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP);
4602 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm,
4603 Display::displayCrHgsmiControlCompletion, this);
4604 AssertRC(rc);
4605 if (RT_SUCCESS(rc))
4606 {
4607 if (fCheckPendingViewport)
4608 {
4609 ULONG ul;
4610 for (ul = 0; ul < mcMonitors; ul++)
4611 {
4612 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
4613 if (!pFb->pendingViewportInfo.fPending)
4614 continue;
4615
4616 rc = crViewportNotify(ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y,
4617 pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
4618 if (RT_SUCCESS(rc))
4619 pFb->pendingViewportInfo.fPending = false;
4620 else
4621 {
4622 AssertMsgFailed(("crViewportNotify failed %d\n", rc));
4623 rc = VINF_SUCCESS;
4624 }
4625 }
4626 }
4627 return;
4628 }
4629 }
4630 else
4631 rc = VERR_INVALID_STATE;
4632 }
4633
4634 /* we are here because something went wrong with command processing, complete it */
4635 handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
4636}
4637
4638DECLCALLBACK(void) Display::displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd,
4639 uint32_t cbCmd)
4640{
4641 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4642
4643 pDrv->pDisplay->handleCrHgsmiCommandProcess(pCmd, cbCmd);
4644}
4645
4646DECLCALLBACK(void) Display::displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd,
4647 uint32_t cbCmd)
4648{
4649 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4650
4651 pDrv->pDisplay->handleCrHgsmiControlProcess(pCmd, cbCmd);
4652}
4653
4654DECLCALLBACK(void) Display::displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4655 void *pvContext)
4656{
4657 AssertMsgFailed(("not expected!"));
4658 Display *pDisplay = (Display *)pvContext;
4659 pDisplay->handleCrHgsmiCommandCompletion(result, u32Function, pParam);
4660}
4661
4662DECLCALLBACK(void) Display::displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4663 void *pvContext)
4664{
4665 Display *pDisplay = (Display *)pvContext;
4666 pDisplay->handleCrHgsmiControlCompletion(result, u32Function, pParam);
4667
4668}
4669#endif
4670
4671#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4672DECLCALLBACK(void) Display::displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4673 void *pvContext)
4674{
4675 VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
4676 if (pCmd->u.pfnInternal)
4677 ((PFNCRCTLCOMPLETION)pCmd->u.pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
4678}
4679
4680int Display::handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
4681 PFNCRCTLCOMPLETION pfnCompletion,
4682 void *pvCompletion)
4683{
4684 VMMDev *pVMMDev = mParent ? mParent->getVMMDev() : NULL;
4685 if (!pVMMDev)
4686 {
4687 AssertMsgFailed(("no vmmdev\n"));
4688 return VERR_INVALID_STATE;
4689 }
4690
4691 Assert(mhCrOglSvc);
4692 VBOXHGCMSVCPARM parm;
4693 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4694 parm.u.pointer.addr = pCmd;
4695 parm.u.pointer.size = cbCmd;
4696
4697 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
4698 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, displayCrHgcmCtlSubmitCompletion,
4699 pvCompletion);
4700 if (!RT_SUCCESS(rc))
4701 AssertMsgFailed(("hgcmHostFastCallAsync failed rc %d\n", rc));
4702
4703 return rc;
4704}
4705
4706DECLCALLBACK(int) Display::displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
4707 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
4708 PFNCRCTLCOMPLETION pfnCompletion,
4709 void *pvCompletion)
4710{
4711 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4712 Display *pThis = pDrv->pDisplay;
4713 return pThis->handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
4714}
4715
4716int Display::crCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, PFNCRCTLCOMPLETION pfnCompletion, void *pvCompletion)
4717{
4718 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4719 if (RT_SUCCESS(rc))
4720 {
4721 if (mhCrOglSvc)
4722 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmit(mpDrv->pVBVACallbacks, pCmd, cbCmd, pfnCompletion, pvCompletion);
4723 else
4724 rc = VERR_NOT_SUPPORTED;
4725
4726 RTCritSectRwLeaveShared(&mCrOglLock);
4727 }
4728 return rc;
4729}
4730
4731int Display::crCtlSubmitSync(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4732{
4733 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4734 if (RT_SUCCESS(rc))
4735 {
4736 if (mhCrOglSvc)
4737 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmitSync(mpDrv->pVBVACallbacks, pCmd, cbCmd);
4738 else
4739 rc = VERR_NOT_SUPPORTED;
4740
4741 RTCritSectRwLeaveShared(&mCrOglLock);
4742 }
4743 return rc;
4744}
4745
4746int Display::crCtlSubmitAsyncCmdCopy(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4747{
4748 VBOXCRCMDCTL* pCmdCopy = (VBOXCRCMDCTL*)RTMemAlloc(cbCmd);
4749 if (!pCmdCopy)
4750 {
4751 LogRel(("RTMemAlloc failed\n"));
4752 return VERR_NO_MEMORY;
4753 }
4754
4755 memcpy(pCmdCopy, pCmd, cbCmd);
4756
4757 int rc = crCtlSubmit(pCmdCopy, cbCmd, displayCrCmdFree, pCmdCopy);
4758 if (RT_FAILURE(rc))
4759 {
4760 LogRel(("crCtlSubmit failed %d\n", rc));
4761 RTMemFree(pCmdCopy);
4762 return rc;
4763 }
4764
4765 return VINF_SUCCESS;
4766}
4767
4768int Display::crCtlSubmitSyncIfHasDataForScreen(uint32_t u32ScreenID, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4769{
4770 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4771 AssertRCReturn(rc, rc);
4772
4773 if (mCrOglCallbacks.pfnHasDataForScreen && mCrOglCallbacks.pfnHasDataForScreen(u32ScreenID))
4774 rc = crCtlSubmitSync(pCmd, cbCmd);
4775 else
4776 rc = crCtlSubmitAsyncCmdCopy(pCmd, cbCmd);
4777
4778 RTCritSectRwLeaveShared(&mCrOglLock);
4779
4780 return rc;
4781}
4782
4783bool Display::handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64TimeStamp)
4784{
4785# if VBOX_WITH_VPX
4786 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64TimeStamp);
4787# else
4788 return false;
4789# endif
4790}
4791
4792void Display::handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64TimeStamp)
4793{
4794}
4795
4796void Display::handleCrVRecScreenshotPerform(uint32_t uScreen,
4797 uint32_t x, uint32_t y, uint32_t uPixelFormat,
4798 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4799 uint32_t uGuestWidth, uint32_t uGuestHeight,
4800 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4801{
4802 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4803# if VBOX_WITH_VPX
4804 int rc = VideoRecCopyToIntBuf(mpVideoRecCtx, uScreen, x, y,
4805 uPixelFormat,
4806 uBitsPerPixel, uBytesPerLine,
4807 uGuestWidth, uGuestHeight,
4808 pu8BufferAddress, u64TimeStamp);
4809 Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
4810# endif
4811}
4812
4813void Display::handleVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4814{
4815 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4816 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4817}
4818
4819DECLCALLBACK(void) Display::displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
4820 uint32_t x, uint32_t y,
4821 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4822 uint32_t uGuestWidth, uint32_t uGuestHeight,
4823 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4824{
4825 Display *pDisplay = (Display *)pvCtx;
4826 pDisplay->handleCrVRecScreenshotPerform(uScreen,
4827 x, y, FramebufferPixelFormat_FOURCC_RGB, uBitsPerPixel,
4828 uBytesPerLine, uGuestWidth, uGuestHeight,
4829 pu8BufferAddress, u64TimeStamp);
4830}
4831
4832DECLCALLBACK(bool) Display::displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4833{
4834 Display *pDisplay = (Display *)pvCtx;
4835 return pDisplay->handleCrVRecScreenshotBegin(uScreen, u64TimeStamp);
4836}
4837
4838DECLCALLBACK(void) Display::displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4839{
4840 Display *pDisplay = (Display *)pvCtx;
4841 pDisplay->handleCrVRecScreenshotEnd(uScreen, u64TimeStamp);
4842}
4843
4844DECLCALLBACK(void) Display::displayVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4845{
4846 Display *pDisplay = (Display *)pvContext;
4847 pDisplay->handleVRecCompletion(result, u32Function, pParam, pvContext);
4848}
4849
4850#endif
4851
4852
4853#ifdef VBOX_WITH_HGSMI
4854DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags, bool fRenderThreadMode)
4855{
4856 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4857
4858 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4859 Display *pThis = pDrv->pDisplay;
4860
4861 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
4862 {
4863 LogRel(("enabling different vbva mode"));
4864#ifdef DEBUG_misha
4865 AssertMsgFailed(("enabling different vbva mode"));
4866#endif
4867 return VERR_INVALID_STATE;
4868 }
4869
4870 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
4871 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
4872 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
4873 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
4874
4875 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
4876
4877 return VINF_SUCCESS;
4878}
4879
4880DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4881{
4882 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4883
4884 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4885 Display *pThis = pDrv->pDisplay;
4886
4887 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4888
4889 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
4890
4891 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4892 {
4893 /* Make sure that the primary screen is visible now.
4894 * The guest can't use VBVA anymore, so only only the VGA device output works.
4895 */
4896 if (pFBInfo->fDisabled)
4897 {
4898 pFBInfo->fDisabled = false;
4899 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4900 GuestMonitorChangedEventType_Enabled,
4901 uScreenId,
4902 pFBInfo->xOrigin, pFBInfo->yOrigin,
4903 pFBInfo->w, pFBInfo->h);
4904 }
4905 }
4906
4907 pFBInfo->fVBVAEnabled = false;
4908 pFBInfo->fVBVAForceResize = false;
4909 pFBInfo->fRenderThreadMode = false;
4910
4911 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
4912
4913 pFBInfo->pVBVAHostFlags = NULL;
4914
4915 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4916 {
4917 /* Force full screen update, because VGA device must take control, do resize, etc. */
4918 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort);
4919 }
4920}
4921
4922DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4923{
4924 LogFlowFunc(("uScreenId %d\n", uScreenId));
4925
4926 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4927 Display *pThis = pDrv->pDisplay;
4928 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4929
4930 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
4931 {
4932 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
4933 pThis->mcMonitors);
4934 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
4935 }
4936
4937 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
4938 {
4939 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
4940 {
4941 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
4942 * under display device lock, so thread safe.
4943 */
4944 pFBInfo->cVBVASkipUpdate = 0;
4945 pThis->handleDisplayUpdate(uScreenId, pFBInfo->vbvaSkippedRect.xLeft - pFBInfo->xOrigin,
4946 pFBInfo->vbvaSkippedRect.yTop - pFBInfo->yOrigin,
4947 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
4948 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
4949 }
4950 }
4951 else
4952 {
4953 /* The framebuffer is being resized. */
4954 pFBInfo->cVBVASkipUpdate++;
4955 }
4956}
4957
4958DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
4959 const PVBVACMDHDR pCmd, size_t cbCmd)
4960{
4961 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
4962
4963 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4964 Display *pThis = pDrv->pDisplay;
4965 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4966
4967 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
4968 {
4969 if (pFBInfo->fDefaultFormat)
4970 {
4971 /* Make sure that framebuffer contains the same image as the guest VRAM. */
4972 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
4973 && !pFBInfo->fDisabled)
4974 {
4975 pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
4976 }
4977 else if ( !pFBInfo->pSourceBitmap.isNull()
4978 && !pFBInfo->fDisabled)
4979 {
4980 /* Render VRAM content to the framebuffer. */
4981 BYTE *pAddress = NULL;
4982 ULONG ulWidth = 0;
4983 ULONG ulHeight = 0;
4984 ULONG ulBitsPerPixel = 0;
4985 ULONG ulBytesPerLine = 0;
4986 ULONG ulPixelFormat = 0;
4987
4988 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
4989 &ulWidth,
4990 &ulHeight,
4991 &ulBitsPerPixel,
4992 &ulBytesPerLine,
4993 &ulPixelFormat);
4994 if (SUCCEEDED(hrc))
4995 {
4996 uint32_t width = pCmd->w;
4997 uint32_t height = pCmd->h;
4998
4999 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
5000 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
5001 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
5002 uint32_t u32SrcWidth = pFBInfo->w;
5003 uint32_t u32SrcHeight = pFBInfo->h;
5004 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
5005 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
5006
5007 uint8_t *pu8Dst = pAddress;
5008 int32_t xDst = xSrc;
5009 int32_t yDst = ySrc;
5010 uint32_t u32DstWidth = u32SrcWidth;
5011 uint32_t u32DstHeight = u32SrcHeight;
5012 uint32_t u32DstLineSize = u32DstWidth * 4;
5013 uint32_t u32DstBitsPerPixel = 32;
5014
5015 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
5016 width, height,
5017 pu8Src,
5018 xSrc, ySrc,
5019 u32SrcWidth, u32SrcHeight,
5020 u32SrcLineSize, u32SrcBitsPerPixel,
5021 pu8Dst,
5022 xDst, yDst,
5023 u32DstWidth, u32DstHeight,
5024 u32DstLineSize, u32DstBitsPerPixel);
5025 }
5026 }
5027 }
5028
5029 VBVACMDHDR hdrSaved = *pCmd;
5030
5031 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
5032
5033 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
5034 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
5035
5036 /* @todo new SendUpdate entry which can get a separate cmd header or coords. */
5037 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, (uint32_t)cbCmd);
5038
5039 *pHdrUnconst = hdrSaved;
5040 }
5041}
5042
5043DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
5044 uint32_t cx, uint32_t cy)
5045{
5046 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
5047
5048 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
5049 Display *pThis = pDrv->pDisplay;
5050 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
5051
5052 /* @todo handleFramebufferUpdate (uScreenId,
5053 * x - pThis->maFramebuffers[uScreenId].xOrigin,
5054 * y - pThis->maFramebuffers[uScreenId].yOrigin,
5055 * cx, cy);
5056 */
5057 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
5058 {
5059 pThis->handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
5060 }
5061 else
5062 {
5063 /* Save the updated rectangle. */
5064 int32_t xRight = x + cx;
5065 int32_t yBottom = y + cy;
5066
5067 if (pFBInfo->cVBVASkipUpdate == 1)
5068 {
5069 pFBInfo->vbvaSkippedRect.xLeft = x;
5070 pFBInfo->vbvaSkippedRect.yTop = y;
5071 pFBInfo->vbvaSkippedRect.xRight = xRight;
5072 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
5073 }
5074 else
5075 {
5076 if (pFBInfo->vbvaSkippedRect.xLeft > x)
5077 {
5078 pFBInfo->vbvaSkippedRect.xLeft = x;
5079 }
5080 if (pFBInfo->vbvaSkippedRect.yTop > y)
5081 {
5082 pFBInfo->vbvaSkippedRect.yTop = y;
5083 }
5084 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
5085 {
5086 pFBInfo->vbvaSkippedRect.xRight = xRight;
5087 }
5088 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
5089 {
5090 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
5091 }
5092 }
5093 }
5094}
5095
5096#ifdef DEBUG_sunlover
5097static void logVBVAResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
5098{
5099 LogRel(("displayVBVAResize: [%d] %s\n"
5100 " pView->u32ViewIndex %d\n"
5101 " pView->u32ViewOffset 0x%08X\n"
5102 " pView->u32ViewSize 0x%08X\n"
5103 " pView->u32MaxScreenSize 0x%08X\n"
5104 " pScreen->i32OriginX %d\n"
5105 " pScreen->i32OriginY %d\n"
5106 " pScreen->u32StartOffset 0x%08X\n"
5107 " pScreen->u32LineSize 0x%08X\n"
5108 " pScreen->u32Width %d\n"
5109 " pScreen->u32Height %d\n"
5110 " pScreen->u16BitsPerPixel %d\n"
5111 " pScreen->u16Flags 0x%04X\n"
5112 " pFBInfo->u32Offset 0x%08X\n"
5113 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
5114 " pFBInfo->u32InformationSize 0x%08X\n"
5115 " pFBInfo->fDisabled %d\n"
5116 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
5117 " pFBInfo->u16BitsPerPixel %d\n"
5118 " pFBInfo->pu8FramebufferVRAM %p\n"
5119 " pFBInfo->u32LineSize 0x%08X\n"
5120 " pFBInfo->flags 0x%04X\n"
5121 " pFBInfo->pHostEvents %p\n"
5122 " pFBInfo->u32ResizeStatus %d\n"
5123 " pFBInfo->fDefaultFormat %d\n"
5124 " dirtyRect %d-%d %d-%d\n"
5125 " pFBInfo->pendingResize.fPending %d\n"
5126 " pFBInfo->pendingResize.pixelFormat %d\n"
5127 " pFBInfo->pendingResize.pvVRAM %p\n"
5128 " pFBInfo->pendingResize.bpp %d\n"
5129 " pFBInfo->pendingResize.cbLine 0x%08X\n"
5130 " pFBInfo->pendingResize.w,h %dx%d\n"
5131 " pFBInfo->pendingResize.flags 0x%04X\n"
5132 " pFBInfo->fVBVAEnabled %d\n"
5133 " pFBInfo->fVBVAForceResize %d\n"
5134 " pFBInfo->cVBVASkipUpdate %d\n"
5135 " pFBInfo->vbvaSkippedRect %d-%d %d-%d\n"
5136 " pFBInfo->pVBVAHostFlags %p\n"
5137 "",
5138 pScreen->u32ViewIndex,
5139 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
5140 pView->u32ViewIndex,
5141 pView->u32ViewOffset,
5142 pView->u32ViewSize,
5143 pView->u32MaxScreenSize,
5144 pScreen->i32OriginX,
5145 pScreen->i32OriginY,
5146 pScreen->u32StartOffset,
5147 pScreen->u32LineSize,
5148 pScreen->u32Width,
5149 pScreen->u32Height,
5150 pScreen->u16BitsPerPixel,
5151 pScreen->u16Flags,
5152 pFBInfo->u32Offset,
5153 pFBInfo->u32MaxFramebufferSize,
5154 pFBInfo->u32InformationSize,
5155 pFBInfo->fDisabled,
5156 pFBInfo->xOrigin,
5157 pFBInfo->yOrigin,
5158 pFBInfo->w,
5159 pFBInfo->h,
5160 pFBInfo->u16BitsPerPixel,
5161 pFBInfo->pu8FramebufferVRAM,
5162 pFBInfo->u32LineSize,
5163 pFBInfo->flags,
5164 pFBInfo->pHostEvents,
5165 pFBInfo->u32ResizeStatus,
5166 pFBInfo->fDefaultFormat,
5167 pFBInfo->dirtyRect.xLeft,
5168 pFBInfo->dirtyRect.xRight,
5169 pFBInfo->dirtyRect.yTop,
5170 pFBInfo->dirtyRect.yBottom,
5171 pFBInfo->pendingResize.fPending,
5172 pFBInfo->pendingResize.pixelFormat,
5173 pFBInfo->pendingResize.pvVRAM,
5174 pFBInfo->pendingResize.bpp,
5175 pFBInfo->pendingResize.cbLine,
5176 pFBInfo->pendingResize.w,
5177 pFBInfo->pendingResize.h,
5178 pFBInfo->pendingResize.flags,
5179 pFBInfo->fVBVAEnabled,
5180 pFBInfo->fVBVAForceResize,
5181 pFBInfo->cVBVASkipUpdate,
5182 pFBInfo->vbvaSkippedRect.xLeft,
5183 pFBInfo->vbvaSkippedRect.yTop,
5184 pFBInfo->vbvaSkippedRect.xRight,
5185 pFBInfo->vbvaSkippedRect.yBottom,
5186 pFBInfo->pVBVAHostFlags
5187 ));
5188}
5189#endif /* DEBUG_sunlover */
5190
5191DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView,
5192 const PVBVAINFOSCREEN pScreen, void *pvVRAM)
5193{
5194 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
5195
5196 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
5197 Display *pThis = pDrv->pDisplay;
5198
5199 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
5200
5201 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
5202 {
5203 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
5204
5205 pFBInfo->fDisabled = true;
5206 pFBInfo->flags = pScreen->u16Flags;
5207
5208 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
5209 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
5210 * the VM window will be black. */
5211 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
5212 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
5213 pThis->handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
5214 u32Width, u32Height, pScreen->u16Flags);
5215
5216 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5217 GuestMonitorChangedEventType_Disabled,
5218 pScreen->u32ViewIndex,
5219 0, 0, 0, 0);
5220 return VINF_SUCCESS;
5221 }
5222
5223 /* If display was disabled or there is no framebuffer, a resize will be required,
5224 * because the framebuffer was/will be changed.
5225 */
5226 bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
5227
5228 if (pFBInfo->fVBVAForceResize)
5229 {
5230 /* VBVA was just enabled. Do the resize. */
5231 fResize = true;
5232 pFBInfo->fVBVAForceResize = false;
5233 }
5234
5235 /* Check if this is a real resize or a notification about the screen origin.
5236 * The guest uses this VBVAResize call for both.
5237 */
5238 fResize = fResize
5239 || pFBInfo->u16BitsPerPixel != pScreen->u16BitsPerPixel
5240 || pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM + pScreen->u32StartOffset
5241 || pFBInfo->u32LineSize != pScreen->u32LineSize
5242 || pFBInfo->w != pScreen->u32Width
5243 || pFBInfo->h != pScreen->u32Height;
5244
5245 bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
5246 || pFBInfo->yOrigin != pScreen->i32OriginY;
5247
5248 if (fNewOrigin || fResize)
5249 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
5250
5251 if (pFBInfo->fDisabled)
5252 {
5253 pFBInfo->fDisabled = false;
5254 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5255 GuestMonitorChangedEventType_Enabled,
5256 pScreen->u32ViewIndex,
5257 pScreen->i32OriginX, pScreen->i32OriginY,
5258 pScreen->u32Width, pScreen->u32Height);
5259 /* Continue to update pFBInfo. */
5260 }
5261
5262 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
5263 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
5264 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
5265
5266 pFBInfo->xOrigin = pScreen->i32OriginX;
5267 pFBInfo->yOrigin = pScreen->i32OriginY;
5268
5269 pFBInfo->w = pScreen->u32Width;
5270 pFBInfo->h = pScreen->u32Height;
5271
5272 pFBInfo->u16BitsPerPixel = pScreen->u16BitsPerPixel;
5273 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM + pScreen->u32StartOffset;
5274 pFBInfo->u32LineSize = pScreen->u32LineSize;
5275
5276 pFBInfo->flags = pScreen->u16Flags;
5277
5278 if (fNewOrigin)
5279 {
5280 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5281 GuestMonitorChangedEventType_NewOrigin,
5282 pScreen->u32ViewIndex,
5283 pScreen->i32OriginX, pScreen->i32OriginY,
5284 0, 0);
5285 }
5286
5287 if (!fResize)
5288 {
5289 /* No parameters of the framebuffer have actually changed. */
5290 if (fNewOrigin)
5291 {
5292 /* VRDP server still need this notification. */
5293 LogRelFlowFunc(("Calling VRDP\n"));
5294 pThis->mParent->consoleVRDPServer()->SendResize();
5295 }
5296 return VINF_SUCCESS;
5297 }
5298
5299 /* Do a regular resize. */
5300 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
5301 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
5302 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags);
5303}
5304
5305DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
5306 uint32_t xHot, uint32_t yHot,
5307 uint32_t cx, uint32_t cy,
5308 const void *pvShape)
5309{
5310 LogFlowFunc(("\n"));
5311
5312 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
5313 Display *pThis = pDrv->pDisplay;
5314
5315 size_t cbShapeSize = 0;
5316
5317 if (pvShape)
5318 {
5319 cbShapeSize = (cx + 7) / 8 * cy; /* size of the AND mask */
5320 cbShapeSize = ((cbShapeSize + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
5321 }
5322 com::SafeArray<BYTE> shapeData(cbShapeSize);
5323
5324 if (pvShape)
5325 ::memcpy(shapeData.raw(), pvShape, cbShapeSize);
5326
5327 /* Tell the console about it */
5328 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
5329 xHot, yHot, cx, cy, ComSafeArrayAsInParam(shapeData));
5330
5331 return VINF_SUCCESS;
5332}
5333#endif /* VBOX_WITH_HGSMI */
5334
5335/**
5336 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
5337 */
5338DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
5339{
5340 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
5341 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5342 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
5343 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
5344 return NULL;
5345}
5346
5347
5348/**
5349 * Destruct a display driver instance.
5350 *
5351 * @returns VBox status.
5352 * @param pDrvIns The driver instance data.
5353 */
5354DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
5355{
5356 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
5357 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5358 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
5359
5360 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
5361
5362 pThis->IConnector.pu8Data = NULL;
5363 pThis->IConnector.cbScanline = 0;
5364 pThis->IConnector.cBits = 32;
5365 pThis->IConnector.cx = 0;
5366 pThis->IConnector.cy = 0;
5367
5368 if (pThis->pDisplay)
5369 {
5370 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
5371#ifdef VBOX_WITH_VPX
5372 pThis->pDisplay->VideoCaptureStop();
5373#endif
5374#ifdef VBOX_WITH_CRHGSMI
5375 pThis->pDisplay->destructCrHgsmiData();
5376#endif
5377 pThis->pDisplay->mpDrv = NULL;
5378 pThis->pDisplay->mpVMMDev = NULL;
5379 pThis->pDisplay->mLastAddress = NULL;
5380 pThis->pDisplay->mLastBytesPerLine = 0;
5381 pThis->pDisplay->mLastBitsPerPixel = 0,
5382 pThis->pDisplay->mLastWidth = 0;
5383 pThis->pDisplay->mLastHeight = 0;
5384 }
5385}
5386
5387
5388/**
5389 * Construct a display driver instance.
5390 *
5391 * @copydoc FNPDMDRVCONSTRUCT
5392 */
5393DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
5394{
5395 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
5396 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5397 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
5398
5399 /*
5400 * Validate configuration.
5401 */
5402 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
5403 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
5404 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
5405 ("Configuration error: Not possible to attach anything to this driver!\n"),
5406 VERR_PDM_DRVINS_NO_ATTACH);
5407
5408 /*
5409 * Init Interfaces.
5410 */
5411 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
5412
5413 pThis->IConnector.pfnResize = Display::displayResizeCallback;
5414 pThis->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
5415 pThis->IConnector.pfnRefresh = Display::displayRefreshCallback;
5416 pThis->IConnector.pfnReset = Display::displayResetCallback;
5417 pThis->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
5418 pThis->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
5419 pThis->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
5420#ifdef VBOX_WITH_VIDEOHWACCEL
5421 pThis->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
5422#endif
5423#ifdef VBOX_WITH_CRHGSMI
5424 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
5425 pThis->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
5426#endif
5427#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
5428 pThis->IConnector.pfnCrHgcmCtlSubmit = Display::displayCrHgcmCtlSubmit;
5429#endif
5430#ifdef VBOX_WITH_HGSMI
5431 pThis->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
5432 pThis->IConnector.pfnVBVADisable = Display::displayVBVADisable;
5433 pThis->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
5434 pThis->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
5435 pThis->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
5436 pThis->IConnector.pfnVBVAResize = Display::displayVBVAResize;
5437 pThis->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
5438#endif
5439
5440 /*
5441 * Get the IDisplayPort interface of the above driver/device.
5442 */
5443 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
5444 if (!pThis->pUpPort)
5445 {
5446 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
5447 return VERR_PDM_MISSING_INTERFACE_ABOVE;
5448 }
5449#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
5450 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
5451 if (!pThis->pVBVACallbacks)
5452 {
5453 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
5454 return VERR_PDM_MISSING_INTERFACE_ABOVE;
5455 }
5456#endif
5457 /*
5458 * Get the Display object pointer and update the mpDrv member.
5459 */
5460 void *pv;
5461 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
5462 if (RT_FAILURE(rc))
5463 {
5464 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
5465 return rc;
5466 }
5467 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
5468 pThis->pDisplay = pDisplay;
5469 pThis->pDisplay->mpDrv = pThis;
5470
5471 /* Disable VRAM to a buffer copy initially. */
5472 pThis->pUpPort->pfnSetRenderVRAM (pThis->pUpPort, false);
5473 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
5474
5475 /*
5476 * Start periodic screen refreshes
5477 */
5478 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
5479
5480#ifdef VBOX_WITH_CRHGSMI
5481 pDisplay->setupCrHgsmiData();
5482#endif
5483
5484#ifdef VBOX_WITH_VPX
5485 ComPtr<IMachine> pMachine = pDisplay->mParent->machine();
5486 BOOL fEnabled = false;
5487 HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
5488 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
5489 if (fEnabled)
5490 {
5491 rc = pDisplay->VideoCaptureStart();
5492 fireVideoCaptureChangedEvent(pDisplay->mParent->getEventSource());
5493 }
5494#endif
5495
5496 return rc;
5497}
5498
5499
5500/**
5501 * Display driver registration record.
5502 */
5503const PDMDRVREG Display::DrvReg =
5504{
5505 /* u32Version */
5506 PDM_DRVREG_VERSION,
5507 /* szName */
5508 "MainDisplay",
5509 /* szRCMod */
5510 "",
5511 /* szR0Mod */
5512 "",
5513 /* pszDescription */
5514 "Main display driver (Main as in the API).",
5515 /* fFlags */
5516 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5517 /* fClass. */
5518 PDM_DRVREG_CLASS_DISPLAY,
5519 /* cMaxInstances */
5520 ~0U,
5521 /* cbInstance */
5522 sizeof(DRVMAINDISPLAY),
5523 /* pfnConstruct */
5524 Display::drvConstruct,
5525 /* pfnDestruct */
5526 Display::drvDestruct,
5527 /* pfnRelocate */
5528 NULL,
5529 /* pfnIOCtl */
5530 NULL,
5531 /* pfnPowerOn */
5532 NULL,
5533 /* pfnReset */
5534 NULL,
5535 /* pfnSuspend */
5536 NULL,
5537 /* pfnResume */
5538 NULL,
5539 /* pfnAttach */
5540 NULL,
5541 /* pfnDetach */
5542 NULL,
5543 /* pfnPowerOff */
5544 NULL,
5545 /* pfnSoftReset */
5546 NULL,
5547 /* u32EndVersion */
5548 PDM_DRVREG_VERSION
5549};
5550/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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