VirtualBox

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

Last change on this file since 2817 was 2817, checked in by vboxsync, 17 years ago

L4: Make sure we don't render into the VRAM while the current console is not active

  • 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 InnoTek Systemberatung 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)
827{
828 PPDMIVMMDEVPORT pVMMDevPort = gVMMDev->getVMMDevPort ();
829
830 if (pVMMDevPort)
831 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aColorDepth);
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.

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