VirtualBox

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

Last change on this file since 21227 was 21227, checked in by vboxsync, 15 years ago

VBoxGuest.h/VMMDev.h/VBoxGuestLib.h usage cleanup.

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