VirtualBox

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

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

Main,Frontends: IFramebuffer::NotifyUpdateImage

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

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