VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBFE/DisplayImpl.cpp@ 3110

Last change on this file since 3110 was 3110, checked in by vboxsync, 18 years ago

Added the display index parameter to the SetVideoModeHint (in the guest all hints are still processed only for the primary monitor).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.6 KB
Line 
1/** @file
2 *
3 * VBox frontends: Basic Frontend (BFE):
4 * Implementation of VMDisplay class
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23#define LOG_GROUP LOG_GROUP_MAIN
24
25#ifdef VBOXBFE_WITHOUT_COM
26# include "COMDefs.h"
27# include <iprt/string.h>
28#else
29# include <VBox/com/defs.h>
30#endif
31
32#include <iprt/alloc.h>
33#include <iprt/semaphore.h>
34#include <iprt/thread.h>
35#include <VBox/pdm.h>
36#include <VBox/VBoxGuest.h>
37#include <VBox/cfgm.h>
38#include <VBox/err.h>
39#include <iprt/assert.h>
40#include <VBox/log.h>
41#include <iprt/asm.h>
42
43#ifdef __L4__
44#include <stdio.h>
45#include <l4/util/util.h>
46#include <l4/log/l4log.h>
47#endif
48
49#include "DisplayImpl.h"
50#include "Framebuffer.h"
51#include "VMMDevInterface.h"
52
53
54/*******************************************************************************
55* Structures and Typedefs *
56*******************************************************************************/
57
58/**
59 * VMDisplay driver instance data.
60 */
61typedef struct DRVMAINDISPLAY
62{
63 /** Pointer to the display object. */
64 VMDisplay *pDisplay;
65 /** Pointer to the driver instance structure. */
66 PPDMDRVINS pDrvIns;
67 /** Pointer to the keyboard port interface of the driver/device above us. */
68 PPDMIDISPLAYPORT pUpPort;
69 /** Our display connector interface. */
70 PDMIDISPLAYCONNECTOR Connector;
71} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
72
73/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
74#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) ( (PDRVMAINDISPLAY) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINDISPLAY, Connector)) )
75
76
77// constructor / destructor
78/////////////////////////////////////////////////////////////////////////////
79
80VMDisplay::VMDisplay()
81{
82 mpDrv = NULL;
83
84 mpVbvaMemory = NULL;
85 mfVideoAccelEnabled = false;
86
87 mpPendingVbvaMemory = NULL;
88 mfPendingVideoAccelEnable = false;
89
90 mfMachineRunning = false;
91
92 mpu8VbvaPartial = NULL;
93 mcbVbvaPartial = 0;
94
95 RTSemEventMultiCreate(&mUpdateSem);
96
97 // reset the event sems
98 RTSemEventMultiReset(mUpdateSem);
99
100 // by default, we have an internal Framebuffer which is
101 // NULL, i.e. a black hole for no display output
102 mFramebuffer = 0;
103 mInternalFramebuffer = true;
104 mFramebufferOpened = false;
105
106 mu32ResizeStatus = ResizeStatus_Void;
107}
108
109VMDisplay::~VMDisplay()
110{
111 mFramebuffer = 0;
112 RTSemEventMultiDestroy(mUpdateSem);
113}
114
115// public methods only for internal purposes
116/////////////////////////////////////////////////////////////////////////////
117
118/**
119 * Handle display resize event.
120 *
121 * @returns COM status code
122 * @param w New display width
123 * @param h New display height
124 */
125int VMDisplay::handleDisplayResize (int w, int h)
126{
127 LogFlow(("VMDisplay::handleDisplayResize(): w=%d, h=%d\n", w, h));
128
129 // if there is no Framebuffer, this call is not interesting
130 if (mFramebuffer == NULL)
131 return VINF_SUCCESS;
132
133 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
134 * disable access to the VGA device by the EMT thread.
135 */
136 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_InProgress, ResizeStatus_Void);
137 AssertRelease(f);NOREF(f);
138
139 // callback into the Framebuffer to notify it
140 BOOL finished;
141
142 mFramebuffer->Lock();
143
144 mFramebuffer->RequestResize(w, h, &finished);
145
146 if (!finished)
147 {
148 LogFlow(("VMDisplay::handleDisplayResize: external framebuffer wants us to wait!\n"));
149
150 /* Note: The previously obtained framebuffer lock must be preserved.
151 * The EMT keeps the framebuffer lock until the resize process completes.
152 */
153
154 return VINF_VGA_RESIZE_IN_PROGRESS;
155 }
156
157 /* Set the status so the 'handleResizeCompleted' would work. */
158 f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
159 AssertRelease(f);NOREF(f);
160
161 /* The method also unlocks the framebuffer. */
162 handleResizeCompletedEMT();
163
164 return VINF_SUCCESS;
165}
166
167/**
168 * Framebuffer has been resized.
169 * Read the new display data and unlock the framebuffer.
170 *
171 * @thread EMT
172 */
173void VMDisplay::handleResizeCompletedEMT (void)
174{
175 LogFlowFunc(("\n"));
176 if (mFramebuffer)
177 {
178 /* Framebuffer has completed the resize. Update the connector data. */
179 updateDisplayData();
180
181 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, true);
182
183 /* Unlock framebuffer. */
184 mFramebuffer->Unlock();
185 }
186
187 /* Go into non resizing state. */
188 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
189 AssertRelease(f);NOREF(f);
190}
191
192/**
193 * Notification that the framebuffer has completed the
194 * asynchronous resize processing
195 *
196 * @returns COM status code
197 */
198STDMETHODIMP VMDisplay::ResizeCompleted()
199{
200 LogFlow(("VMDisplay::ResizeCompleted\n"));
201
202 // this is only valid for external framebuffers
203 if (mInternalFramebuffer)
204 return E_FAIL;
205
206 /* Set the flag indicating that the resize has completed and display data need to be updated. */
207 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
208 AssertRelease(f);NOREF(f);
209
210 return S_OK;
211}
212
213static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
214{
215 /* Correct negative x and y coordinates. */
216 if (*px < 0)
217 {
218 *px += *pw; /* Compute xRight which is also the new width. */
219 *pw = (*px < 0) ? 0: *px;
220 *px = 0;
221 }
222
223 if (*py < 0)
224 {
225 *py += *ph; /* Compute xBottom, which is also the new height. */
226 *ph = (*py < 0) ? 0: *py;
227 *py = 0;
228 }
229
230 /* Also check if coords are greater than the display resolution. */
231 if (*px + *pw > cx)
232 *pw = cx > *px ? cx - *px: 0;
233
234 if (*py + *ph > cy)
235 *ph = cy > *py ? cy - *py: 0;
236}
237
238/**
239 * Handle display update
240 *
241 * @returns COM status code
242 * @param w New display width
243 * @param h New display height
244 */
245void VMDisplay::handleDisplayUpdate (int x, int y, int w, int h)
246{
247 // if there is no Framebuffer, this call is not interesting
248 if (mFramebuffer == NULL)
249 return;
250
251 mFramebuffer->Lock();
252
253 checkCoordBounds (&x, &y, &w, &h, mpDrv->Connector.cx, mpDrv->Connector.cy);
254
255 // special processing for the internal Framebuffer
256 if (mInternalFramebuffer)
257 {
258 mFramebuffer->Unlock();
259 }
260 else
261 {
262 // callback into the Framebuffer to notify it
263 BOOL finished;
264
265 RTSemEventMultiReset(mUpdateSem);
266
267 mFramebuffer->NotifyUpdate(x, y, w, h, &finished);
268 mFramebuffer->Unlock();
269
270 if (!finished)
271 {
272 // the Framebuffer needs more time to process
273 // the event so we have to halt the VM until it's done
274 RTSemEventMultiWait(mUpdateSem, RT_INDEFINITE_WAIT);
275 }
276 }
277}
278
279// IDisplay properties
280/////////////////////////////////////////////////////////////////////////////
281
282/**
283 * Returns the current display width in pixel
284 *
285 * @returns COM status code
286 * @param width Address of result variable.
287 */
288uint32_t VMDisplay::getWidth()
289{
290 Assert(mpDrv);
291 return mpDrv->Connector.cx;
292}
293
294/**
295 * Returns the current display height in pixel
296 *
297 * @returns COM status code
298 * @param height Address of result variable.
299 */
300uint32_t VMDisplay::getHeight()
301{
302 Assert(mpDrv);
303 return mpDrv->Connector.cy;
304}
305
306/**
307 * Returns the current display color depth in bits
308 *
309 * @returns COM status code
310 * @param colorDepth Address of result variable.
311 */
312uint32_t VMDisplay::getColorDepth()
313{
314 Assert(mpDrv);
315 return mpDrv->Connector.cBits;
316}
317
318void VMDisplay::updatePointerShape(bool fVisible, bool fAlpha, uint32_t xHot, uint32_t yHot, uint32_t width, uint32_t height, void *pShape)
319{
320}
321
322
323// IDisplay methods
324/////////////////////////////////////////////////////////////////////////////
325
326/**
327 * Registers an external Framebuffer
328 *
329 * @returns COM status code
330 * @param Framebuffer external Framebuffer object
331 */
332STDMETHODIMP VMDisplay::RegisterExternalFramebuffer(Framebuffer *Framebuffer)
333{
334 if (!Framebuffer)
335 return E_POINTER;
336
337 // free current Framebuffer (if there is any)
338 mFramebuffer = 0;
339 mInternalFramebuffer = false;
340 mFramebuffer = Framebuffer;
341 updateDisplayData();
342 return S_OK;
343}
344
345/* InvalidateAndUpdate schedules a request that eventually calls */
346/* mpDrv->pUpPort->pfnUpdateDisplayAll which in turns accesses the */
347/* framebuffer. In order to synchronize with other framebuffer */
348/* related activities this call needs to be framed by Lock/Unlock. */
349void
350VMDisplay::doInvalidateAndUpdate(struct DRVMAINDISPLAY *mpDrv)
351{
352 mpDrv->pDisplay->mFramebuffer->Lock();
353 mpDrv->pUpPort->pfnUpdateDisplayAll( mpDrv->pUpPort);
354 mpDrv->pDisplay->mFramebuffer->Unlock();
355}
356
357/**
358 * Does a full invalidation of the VM display and instructs the VM
359 * to update it immediately.
360 *
361 * @returns COM status code
362 */
363STDMETHODIMP VMDisplay::InvalidateAndUpdate()
364{
365 LogFlow (("VMDisplay::InvalidateAndUpdate(): BEGIN\n"));
366
367 HRESULT rc = S_OK;
368
369 LogFlow (("VMDisplay::InvalidateAndUpdate(): sending DPYUPDATE request\n"));
370
371 Assert(pVM);
372 /* pdm.h says that this has to be called from the EMT thread */
373 PVMREQ pReq;
374 int rcVBox = VMR3ReqCallVoid(pVM, &pReq, RT_INDEFINITE_WAIT,
375 (PFNRT)VMDisplay::doInvalidateAndUpdate, 1, mpDrv);
376 if (VBOX_SUCCESS(rcVBox))
377 VMR3ReqFree(pReq);
378
379 if (VBOX_FAILURE(rcVBox))
380 rc = E_FAIL;
381
382 LogFlow (("VMDisplay::InvalidateAndUpdate(): END: rc=%08X\n", rc));
383 return rc;
384}
385
386// private methods
387/////////////////////////////////////////////////////////////////////////////
388
389/**
390 * Helper to update the display information from the Framebuffer
391 *
392 */
393void VMDisplay::updateDisplayData()
394{
395
396 while(!mFramebuffer)
397 {
398#if __L4__
399 asm volatile ("nop":::"memory");
400 l4_sleep(5);
401#else
402 RTThreadYield();
403#endif
404 }
405 Assert(mFramebuffer);
406 // the driver might not have been constructed yet
407 if (mpDrv)
408 {
409 mFramebuffer->getAddress ((uintptr_t *)&mpDrv->Connector.pu8Data);
410 mFramebuffer->getLineSize ((ULONG*)&mpDrv->Connector.cbScanline);
411 mFramebuffer->getColorDepth ((ULONG*)&mpDrv->Connector.cBits);
412 mFramebuffer->getWidth ((ULONG*)&mpDrv->Connector.cx);
413 mFramebuffer->getHeight ((ULONG*)&mpDrv->Connector.cy);
414 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort,
415 !!(mpDrv->Connector.pu8Data != (uint8_t*)~0UL));
416 }
417}
418
419void VMDisplay::resetFramebuffer()
420{
421 if (!mFramebuffer)
422 return;
423
424 // the driver might not have been constructed yet
425 if (mpDrv)
426 {
427 mFramebuffer->getAddress ((uintptr_t *)&mpDrv->Connector.pu8Data);
428 mFramebuffer->getColorDepth ((ULONG*)&mpDrv->Connector.cBits);
429 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort,
430 !!(mpDrv->Connector.pu8Data != (uint8_t*)~0UL));
431 }
432}
433
434/**
435 * Handle display resize event
436 *
437 * @param pInterface VMDisplay connector.
438 * @param cx New width in pixels.
439 * @param cy New height in pixels.
440 */
441DECLCALLBACK(int) VMDisplay::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
442{
443 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
444
445 // forward call to instance handler
446 return pDrv->pDisplay->handleDisplayResize(cx, cy);
447}
448
449/**
450 * Handle display update
451 *
452 * @param pInterface VMDisplay connector.
453 * @param x Left upper boundary x.
454 * @param y Left upper boundary y.
455 * @param cx Update rect width.
456 * @param cy Update rect height.
457 */
458DECLCALLBACK(void) VMDisplay::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
459 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
460{
461 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
462
463 // forward call to instance handler
464 pDrv->pDisplay->handleDisplayUpdate(x, y, cx, cy);
465}
466
467/**
468 * Periodic display refresh callback.
469 *
470 * @param pInterface VMDisplay connector.
471 */
472DECLCALLBACK(void) VMDisplay::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
473{
474 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
475
476
477 /* Contrary to displayUpdateCallback and displayResizeCallback
478 * the framebuffer lock must be taken since the the function
479 * pointed to by pDrv->pUpPort->pfnUpdateDisplay is anaware
480 * of any locking issues. */
481
482 VMDisplay *pDisplay = pDrv->pDisplay;
483
484 uint32_t u32ResizeStatus = pDisplay->mu32ResizeStatus;
485
486 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
487 {
488#ifdef DEBUG_sunlover
489 LogFlowFunc (("ResizeStatus_UpdateDisplayData\n"));
490#endif /* DEBUG_sunlover */
491 /* The framebuffer was resized and display data need to be updated. */
492 pDisplay->handleResizeCompletedEMT ();
493 /* Continue with normal processing because the status here is ResizeStatus_Void. */
494 Assert (pDisplay->mu32ResizeStatus == ResizeStatus_Void);
495 }
496 else if (u32ResizeStatus == ResizeStatus_InProgress)
497 {
498#ifdef DEBUG_sunlover
499 LogFlowFunc (("ResizeStatus_InProcess\n"));
500#endif /* DEBUG_sunlover */
501 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
502 return;
503 }
504
505 if (pDisplay->mfPendingVideoAccelEnable)
506 {
507 /* Acceleration was enabled while machine was not yet running
508 * due to restoring from saved state. Update entire display and
509 * actually enable acceleration.
510 */
511 Assert(pDisplay->mpPendingVbvaMemory);
512
513 /* Acceleration can not be yet enabled.*/
514 Assert(pDisplay->mpVbvaMemory == NULL);
515 Assert(!pDisplay->mfVideoAccelEnabled);
516
517 if (pDisplay->mfMachineRunning)
518 {
519 pDisplay->VideoAccelEnable (pDisplay->mfPendingVideoAccelEnable, pDisplay->mpPendingVbvaMemory);
520
521 /* Reset the pending state. */
522 pDisplay->mfPendingVideoAccelEnable = false;
523 pDisplay->mpPendingVbvaMemory = NULL;
524 }
525 }
526 else
527 {
528 Assert(pDisplay->mpPendingVbvaMemory == NULL);
529
530 if (pDisplay->mfVideoAccelEnabled)
531 {
532 Assert(pDisplay->mpVbvaMemory);
533 pDisplay->VideoAccelFlush ();
534 }
535 else
536 {
537 Assert(pDrv->Connector.pu8Data);
538 pDisplay->mFramebuffer->Lock();
539 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
540 pDisplay->mFramebuffer->Unlock();
541 }
542 }
543}
544
545/**
546 * Reset notification
547 *
548 * @param pInterface Display connector.
549 */
550DECLCALLBACK(void) VMDisplay::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
551{
552 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
553
554 LogFlow(("Display::displayResetCallback\n"));
555
556 /* Disable VBVA mode. */
557 pDrv->pDisplay->VideoAccelEnable (false, NULL);
558}
559
560/**
561 * LFBModeChange notification
562 *
563 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
564 */
565DECLCALLBACK(void) VMDisplay::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
566{
567 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
568
569 LogFlow(("Display::displayLFBModeChangeCallback: %d\n", fEnabled));
570
571 NOREF(fEnabled);
572
573 /**
574 * @todo: If we got the callback then VM if definitely running.
575 * But a better method should be implemented.
576 */
577 pDrv->pDisplay->mfMachineRunning = true;
578
579 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
580 pDrv->pDisplay->VideoAccelEnable (false, NULL);
581}
582
583typedef struct _VBVADIRTYREGION
584{
585 /* Copies of object's pointers used by vbvaRgn functions. */
586 Framebuffer *pFramebuffer;
587 VMDisplay *pDisplay;
588 PPDMIDISPLAYPORT pPort;
589
590 /* Merged rectangles. */
591 int32_t xLeft;
592 int32_t xRight;
593 int32_t yTop;
594 int32_t yBottom;
595
596} VBVADIRTYREGION;
597
598void vbvaRgnInit (VBVADIRTYREGION *prgn, Framebuffer *pfb, VMDisplay *pd, PPDMIDISPLAYPORT pp)
599{
600 memset (prgn, 0, sizeof (VBVADIRTYREGION));
601
602 prgn->pFramebuffer = pfb;
603 prgn->pDisplay = pd;
604 prgn->pPort = pp;
605
606 return;
607}
608
609void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, VBVACMDHDR *phdr)
610{
611 LogFlow(("vbvaRgnDirtyRect: x = %d, y = %d, w = %d, h = %d\n", phdr->x, phdr->y, phdr->w, phdr->h));
612
613 /*
614 * Here update rectangles are accumulated to form an update area.
615 * @todo
616 * Now the simplies method is used which builds one rectangle that
617 * includes all update areas. A bit more advanced method can be
618 * employed here. The method should be fast however.
619 */
620 if (phdr->w == 0 || phdr->h == 0)
621 {
622 /* Empty rectangle. */
623 return;
624 }
625
626 int32_t xRight = phdr->x + phdr->w;
627 int32_t yBottom = phdr->y + phdr->h;
628
629 if (prgn->xRight == 0)
630 {
631 /* This is the first rectangle to be added. */
632 prgn->xLeft = phdr->x;
633 prgn->yTop = phdr->y;
634 prgn->xRight = xRight;
635 prgn->yBottom = yBottom;
636 }
637 else
638 {
639 /* Adjust region coordinates. */
640 if (prgn->xLeft > phdr->x)
641 prgn->xLeft = phdr->x;
642
643 if (prgn->yTop > phdr->y)
644 prgn->yTop = phdr->y;
645
646 if (prgn->xRight < xRight)
647 prgn->xRight = xRight;
648
649 if (prgn->yBottom < yBottom)
650 prgn->yBottom = yBottom;
651 }
652}
653
654void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn)
655{
656 uint32_t w = prgn->xRight - prgn->xLeft;
657 uint32_t h = prgn->yBottom - prgn->yTop;
658
659 if (prgn->pFramebuffer && w != 0 && h != 0)
660 {
661 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, prgn->xLeft, prgn->yTop, w, h);
662 prgn->pDisplay->handleDisplayUpdate (prgn->xLeft, prgn->yTop, w, h);
663 }
664}
665
666static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory, bool fVideoAccelEnabled, bool fVideoAccelVRDP)
667{
668 if (pVbvaMemory)
669 {
670 /* This called only on changes in mode. So reset VRDP always. */
671 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
672
673 if (fVideoAccelEnabled)
674 {
675 fu32Flags |= VBVA_F_MODE_ENABLED;
676
677 if (fVideoAccelVRDP)
678 fu32Flags |= VBVA_F_MODE_VRDP;
679 }
680
681 pVbvaMemory->fu32ModeFlags = fu32Flags;
682 }
683}
684
685bool VMDisplay::VideoAccelAllowed (void)
686{
687 return true;
688}
689
690/**
691 * @thread EMT
692 */
693int VMDisplay::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
694{
695 int rc = VINF_SUCCESS;
696
697 /* Called each time the guest wants to use acceleration,
698 * or when the VGA device disables acceleration,
699 * or when restoring the saved state with accel enabled.
700 *
701 * VGA device disables acceleration on each video mode change
702 * and on reset.
703 *
704 * Guest enabled acceleration at will. And it needs to enable
705 * acceleration after a mode change.
706 */
707 LogFlow(("Display::VideoAccelEnable: mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
708 mfVideoAccelEnabled, fEnable, pVbvaMemory));
709
710 /* Strictly check parameters. Callers must not pass anything in the case. */
711 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
712
713 if (!VideoAccelAllowed ())
714 return VERR_NOT_SUPPORTED;
715
716 /*
717 * Verify that the VM is in running state. If it is not,
718 * then this must be postponed until it goes to running.
719 */
720 if (!mfMachineRunning)
721 {
722 Assert (!mfVideoAccelEnabled);
723
724 LogFlow(("Display::VideoAccelEnable: Machine is not yet running.\n"));
725
726 if (fEnable)
727 {
728 mfPendingVideoAccelEnable = fEnable;
729 mpPendingVbvaMemory = pVbvaMemory;
730 }
731
732 return rc;
733 }
734
735 /* Check that current status is not being changed */
736 if (mfVideoAccelEnabled == fEnable)
737 return rc;
738
739 if (mfVideoAccelEnabled)
740 {
741 /* Process any pending orders and empty the VBVA ring buffer. */
742 VideoAccelFlush ();
743 }
744
745 if (!fEnable && mpVbvaMemory)
746 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
747
748 /* Safety precaution. There is no more VBVA until everything is setup! */
749 mpVbvaMemory = NULL;
750 mfVideoAccelEnabled = false;
751
752 /* Update entire display. */
753 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
754
755 /* Everything OK. VBVA status can be changed. */
756
757 /* Notify the VMMDev, which saves VBVA status in the saved state,
758 * and needs to know current status.
759 */
760 PPDMIVMMDEVPORT pVMMDevPort = gVMMDev->getVMMDevPort ();
761
762 if (pVMMDevPort)
763 pVMMDevPort->pfnVBVAChange (pVMMDevPort, fEnable);
764
765 if (fEnable)
766 {
767 mpVbvaMemory = pVbvaMemory;
768 mfVideoAccelEnabled = true;
769
770 /* Initialize the hardware memory. */
771 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, false);
772 mpVbvaMemory->off32Data = 0;
773 mpVbvaMemory->off32Free = 0;
774
775 memset (mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
776 mpVbvaMemory->indexRecordFirst = 0;
777 mpVbvaMemory->indexRecordFree = 0;
778
779 LogRel(("VBVA: Enabled.\n"));
780 }
781 else
782 {
783 LogRel(("VBVA: Disabled.\n"));
784 }
785
786 LogFlow(("Display::VideoAccelEnable: rc = %Vrc.\n", rc));
787
788 return rc;
789}
790
791static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
792{
793 return true;
794}
795
796static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
797{
798 if (cbDst >= VBVA_RING_BUFFER_SIZE)
799 {
800 AssertFailed ();
801 return;
802 }
803
804 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
805 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
806 int32_t i32Diff = cbDst - u32BytesTillBoundary;
807
808 if (i32Diff <= 0)
809 {
810 /* Chunk will not cross buffer boundary. */
811 memcpy (pu8Dst, src, cbDst);
812 }
813 else
814 {
815 /* Chunk crosses buffer boundary. */
816 memcpy (pu8Dst, src, u32BytesTillBoundary);
817 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
818 }
819
820 /* Advance data offset. */
821 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
822
823 return;
824}
825
826void VMDisplay::SetVideoModeHint(ULONG aWidth, ULONG aHeight, ULONG aColorDepth, ULONG aDisplay)
827{
828 PPDMIVMMDEVPORT pVMMDevPort = gVMMDev->getVMMDevPort ();
829
830 if (pVMMDevPort)
831 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aColorDepth, aDisplay);
832}
833
834static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
835{
836 uint8_t *pu8New;
837
838 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
839 *ppu8, *pcb, cbRecord));
840
841 if (*ppu8)
842 {
843 Assert (*pcb);
844 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
845 }
846 else
847 {
848 Assert (!*pcb);
849 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
850 }
851
852 if (!pu8New)
853 {
854 /* Memory allocation failed, fail the function. */
855 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
856 cbRecord));
857
858 if (*ppu8)
859 RTMemFree (*ppu8);
860
861 *ppu8 = NULL;
862 *pcb = 0;
863
864 return false;
865 }
866
867 /* Fetch data from the ring buffer. */
868 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
869
870 *ppu8 = pu8New;
871 *pcb = cbRecord;
872
873 return true;
874}
875
876/* For contiguous chunks just return the address in the buffer.
877 * For crossing boundary - allocate a buffer from heap.
878 */
879bool VMDisplay::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
880{
881 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
882 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
883
884#ifdef DEBUG_sunlover
885 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd:first = %d, free = %d\n",
886 indexRecordFirst, indexRecordFree));
887#endif /* DEBUG_sunlover */
888
889 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
890 {
891 return false;
892 }
893
894 if (indexRecordFirst == indexRecordFree)
895 {
896 /* No records to process. Return without assigning output variables. */
897 return true;
898 }
899
900 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
901
902#ifdef DEBUG_sunlover
903 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: cbRecord = 0x%08X\n",
904 pRecord->cbRecord));
905#endif /* DEBUG_sunlover */
906
907 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
908
909 if (mcbVbvaPartial)
910 {
911 /* There is a partial read in process. Continue with it. */
912
913 Assert (mpu8VbvaPartial);
914
915 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
916 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
917
918 if (cbRecord > mcbVbvaPartial)
919 {
920 /* New data has been added to the record. */
921 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
922 return false;
923 }
924
925 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
926 {
927 /* The record is completed by guest. Return it to the caller. */
928 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
929 *pcbCmd = mcbVbvaPartial;
930
931 mpu8VbvaPartial = NULL;
932 mcbVbvaPartial = 0;
933
934 /* Advance the record index. */
935 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
936
937#ifdef DEBUG_sunlover
938 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: partial done ok, data = %d, free = %d\n",
939 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
940#endif /* DEBUG_sunlover */
941 }
942
943 return true;
944 }
945
946 /* A new record need to be processed. */
947 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
948 {
949 /* Current record is being written by guest. '=' is important here. */
950 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
951 {
952 /* Partial read must be started. */
953 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
954 return false;
955
956 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
957 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
958 }
959
960 return true;
961 }
962
963 /* Current record is complete. */
964
965 /* The size of largest contiguos chunk in the ring biffer. */
966 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
967
968 /* The ring buffer pointer. */
969 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
970
971 /* The pointer to data in the ring buffer. */
972 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
973
974 /* Fetch or point the data. */
975 if (u32BytesTillBoundary >= cbRecord)
976 {
977 /* The command does not cross buffer boundary. Return address in the buffer. */
978 *ppHdr = (VBVACMDHDR *)src;
979
980 /* Advance data offset. */
981 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
982 }
983 else
984 {
985 /* The command crosses buffer boundary. Rare case, so not optimized. */
986 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
987
988 if (!dst)
989 {
990 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: could not allocate %d bytes from heap!!!\n", cbRecord));
991 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
992 return false;
993 }
994
995 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
996
997 *ppHdr = (VBVACMDHDR *)dst;
998
999#ifdef DEBUG_sunlover
1000 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: Allocated from heap %p\n", dst));
1001#endif /* DEBUG_sunlover */
1002 }
1003
1004 *pcbCmd = cbRecord;
1005
1006 /* Advance the record index. */
1007 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1008
1009#ifdef DEBUG_sunlover
1010 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: done ok, data = %d, free = %d\n",
1011 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1012#endif /* DEBUG_sunlover */
1013
1014 return true;
1015}
1016
1017void VMDisplay::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1018{
1019 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1020
1021 if ( (uint8_t *)pHdr >= au8RingBuffer
1022 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1023 {
1024 /* The pointer is inside ring buffer. Must be continuous chunk. */
1025 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1026
1027 /* Do nothing. */
1028
1029 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1030 }
1031 else
1032 {
1033 /* The pointer is outside. It is then an allocated copy. */
1034
1035#ifdef DEBUG_sunlover
1036 LogFlow(("MAIN::DisplayImpl::vbvaReleaseCmd: Free heap %p\n", pHdr));
1037#endif /* DEBUG_sunlover */
1038
1039 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1040 {
1041 mpu8VbvaPartial = NULL;
1042 mcbVbvaPartial = 0;
1043 }
1044 else
1045 {
1046 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1047 }
1048
1049 RTMemFree (pHdr);
1050 }
1051
1052 return;
1053}
1054
1055/**
1056 * Called regularly on the DisplayRefresh timer.
1057 * Also on behalf of guest, when the ring buffer is full.
1058 *
1059 * @thread EMT
1060 */
1061void VMDisplay::VideoAccelFlush (void)
1062{
1063#ifdef DEBUG_sunlover
1064 LogFlow(("Display::VideoAccelFlush: mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1065#endif /* DEBUG_sunlover */
1066
1067 if (!mfVideoAccelEnabled)
1068 {
1069 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1070 return;
1071 }
1072
1073 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1074 Assert(mpVbvaMemory);
1075
1076#ifdef DEBUG_sunlover
1077 LogFlow(("Display::VideoAccelFlush: indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1078 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1079#endif /* DEBUG_sunlover */
1080
1081 /* Quick check for "nothing to update" case. */
1082 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1083 return;
1084
1085 /* Process the ring buffer */
1086
1087 bool fFramebufferIsNull = (mFramebuffer == NULL);
1088
1089 if (!fFramebufferIsNull)
1090 mFramebuffer->Lock();
1091
1092 /* Initialize dirty rectangles accumulator. */
1093 VBVADIRTYREGION rgn;
1094 vbvaRgnInit (&rgn, mFramebuffer, this, mpDrv->pUpPort);
1095
1096 for (;;)
1097 {
1098 VBVACMDHDR *phdr = NULL;
1099 uint32_t cbCmd = 0;
1100
1101 /* Fetch the command data. */
1102 if (!vbvaFetchCmd (&phdr, &cbCmd))
1103 {
1104 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1105 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1106
1107 /* Disable VBVA on those processing errors. */
1108 VideoAccelEnable (false, NULL);
1109
1110 break;
1111 }
1112
1113 if (!cbCmd)
1114 {
1115 /* No more commands yet in the queue. */
1116 break;
1117 }
1118
1119 if (!fFramebufferIsNull)
1120 {
1121#ifdef DEBUG_sunlover
1122 LogFlow(("MAIN::DisplayImpl::VideoAccelFlush: hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n", cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1123#endif /* DEBUG_sunlover */
1124
1125 /* Handle the command.
1126 *
1127 * Guest is responsible for updating the guest video memory.
1128 * The Windows guest does all drawing using Eng*.
1129 *
1130 * For local output, only dirty rectangle information is used
1131 * to update changed areas.
1132 *
1133 * Dirty rectangles are accumulated to exclude overlapping updates and
1134 * group small updates to a larger one.
1135 */
1136
1137 /* Accumulate the update. */
1138 vbvaRgnDirtyRect (&rgn, phdr);
1139
1140// /* Forward the command to VRDP server. */
1141// mParent->consoleVRDPServer()->SendUpdate (phdr, cbCmd);
1142 }
1143
1144 vbvaReleaseCmd (phdr, cbCmd);
1145 }
1146
1147 if (!fFramebufferIsNull)
1148 mFramebuffer->Unlock ();
1149
1150 /* Draw the framebuffer. */
1151 vbvaRgnUpdateFramebuffer (&rgn);
1152}
1153
1154/**
1155 * Queries an interface to the driver.
1156 *
1157 * @returns Pointer to interface.
1158 * @returns NULL if the interface was not supported by the driver.
1159 * @param pInterface Pointer to this interface structure.
1160 * @param enmInterface The requested interface identification.
1161 */
1162DECLCALLBACK(void *) VMDisplay::drvQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
1163{
1164 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1165 PDRVMAINDISPLAY pDrv = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1166 switch (enmInterface)
1167 {
1168 case PDMINTERFACE_BASE:
1169 return &pDrvIns->IBase;
1170 case PDMINTERFACE_DISPLAY_CONNECTOR:
1171 return &pDrv->Connector;
1172 default:
1173 return NULL;
1174 }
1175}
1176
1177
1178/**
1179 * Construct a display driver instance.
1180 *
1181 * @returns VBox status.
1182 * @param pDrvIns The driver instance data.
1183 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
1184 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
1185 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
1186 * iInstance it's expected to be used a bit in this function.
1187 */
1188DECLCALLBACK(int) VMDisplay::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1189{
1190 PDRVMAINDISPLAY pData = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1191 LogFlow(("VMDisplay::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1192
1193
1194 /*
1195 * Validate configuration.
1196 */
1197 if (!CFGMR3AreValuesValid(pCfgHandle, "Object\0"))
1198 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1199 PPDMIBASE pBaseIgnore;
1200 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseIgnore);
1201 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
1202 {
1203 AssertMsgFailed(("Configuration error: Not possible to attach anything to this driver!\n"));
1204 return VERR_PDM_DRVINS_NO_ATTACH;
1205 }
1206
1207 /*
1208 * Init Interfaces.
1209 */
1210 pDrvIns->IBase.pfnQueryInterface = VMDisplay::drvQueryInterface;
1211
1212 pData->Connector.pfnResize = VMDisplay::displayResizeCallback;
1213 pData->Connector.pfnUpdateRect = VMDisplay::displayUpdateCallback;
1214 pData->Connector.pfnRefresh = VMDisplay::displayRefreshCallback;
1215 pData->Connector.pfnReset = VMDisplay::displayResetCallback;
1216 pData->Connector.pfnLFBModeChange = VMDisplay::displayLFBModeChangeCallback;
1217
1218 /*
1219 * Get the IDisplayPort interface of the above driver/device.
1220 */
1221 pData->pUpPort = (PPDMIDISPLAYPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_PORT);
1222 if (!pData->pUpPort)
1223 {
1224 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
1225 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1226 }
1227
1228 /*
1229 * Get the VMDisplay object pointer and update the mpDrv member.
1230 */
1231 void *pv;
1232 rc = CFGMR3QueryPtr(pCfgHandle, "Object", &pv);
1233 if (VBOX_FAILURE(rc))
1234 {
1235 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Vrc\n", rc));
1236 return rc;
1237 }
1238 pData->pDisplay = (VMDisplay *)pv; /** @todo Check this cast! */
1239 pData->pDisplay->mpDrv = pData;
1240
1241 /*
1242 * If there is a Framebuffer, we have to update our display information
1243 */
1244 if (pData->pDisplay->mFramebuffer)
1245 pData->pDisplay->updateDisplayData();
1246
1247 /*
1248 * Start periodic screen refreshes
1249 */
1250 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 50);
1251
1252 return VINF_SUCCESS;
1253}
1254
1255
1256/**
1257 * VMDisplay driver registration record.
1258 */
1259const PDMDRVREG VMDisplay::DrvReg =
1260{
1261 /* u32Version */
1262 PDM_DRVREG_VERSION,
1263 /* szDriverName */
1264 "MainDisplay",
1265 /* pszDescription */
1266 "Main display driver (Main as in the API).",
1267 /* fFlags */
1268 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1269 /* fClass. */
1270 PDM_DRVREG_CLASS_DISPLAY,
1271 /* cMaxInstances */
1272 ~0,
1273 /* cbInstance */
1274 sizeof(DRVMAINDISPLAY),
1275 /* pfnConstruct */
1276 VMDisplay::drvConstruct,
1277 /* pfnDestruct */
1278 NULL,
1279 /* pfnIOCtl */
1280 NULL,
1281 /* pfnPowerOn */
1282 NULL,
1283 /* pfnReset */
1284 NULL,
1285 /* pfnSuspend */
1286 NULL,
1287 /* pfnResume */
1288 NULL,
1289 /* pfnDetach */
1290 NULL
1291};
Note: See TracBrowser for help on using the repository browser.

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