VirtualBox

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

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

crOpenGL/VideoRec: do hardweight screen capthure only when recorder is ready

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