VirtualBox

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

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

crOpenGL: async notifications

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