VirtualBox

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

Last change on this file since 35748 was 35346, checked in by vboxsync, 14 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

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