VirtualBox

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

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

DisplayImpl: removed obsolete g_StatDisplayRefresh.

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