VirtualBox

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

Last change on this file since 33944 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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