VirtualBox

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

最後變更 在這個檔案從26344是 26173,由 vboxsync 提交於 15 年 前

PDM: s/pCfgHandle/pCfg/g - part 2.

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

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