VirtualBox

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

Last change on this file since 40282 was 40282, checked in by vboxsync, 13 years ago

*: gcc-4.7: ~0 => ~0U in initializers (warning: narrowing conversion of -1' from int' to `unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing])

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