VirtualBox

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

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

IDisplay API changes for multimonitor (xTracker 4655)

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