VirtualBox

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

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

DisplayImpl: removed obsolete framebuffer resize handling.

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

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