VirtualBox

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

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

Do not block EMT while resizing the framebuffer.

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