VirtualBox

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

Last change on this file since 42838 was 42838, checked in by vboxsync, 13 years ago

Main/EncodeAndVideoRecording Module and API implementation: Integrating an independent encoding and video recording module that will serve all the frontends.
Introducing settings settings and API implementation for accessing and modifying video recording parameters:
->target video capture file
->video capture width
->video capture height
->enable video capturing

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

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