VirtualBox

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

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

FE/BFE: more clean-up to reduce the difference between MouseImpl.cpp in Main and VBoxBFE

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