VirtualBox

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

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

DisplayImpl: fixes for legacy VBVA locking and restoring saved state.

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