VirtualBox

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

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

IFramebuffer::capabilities

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