VirtualBox

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

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

DisplayImpl: removed obsolete mLast* fields.

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

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