VirtualBox

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

Last change on this file since 35381 was 35368, checked in by vboxsync, 14 years ago

Main: source re-org.

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