VirtualBox

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

Last change on this file since 44130 was 44130, checked in by vboxsync, 12 years ago

GA/Display: Support for dynamic configuration (position and enable/disable) of the virtual screen for Linux guest.

  • 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 44130 2012-12-14 10:27:28Z 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 aDisplay, BOOL aEnabled,
830 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
831 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
832{
833 PPDMIVMMDEVPORT pVMMDevPort = gVMMDev->getVMMDevPort ();
834
835 if (pVMMDevPort)
836 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
837 aDisplay, aOriginX, aOriginY, aEnabled, aChangeOrigin);
838}
839
840static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
841{
842 uint8_t *pu8New;
843
844 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
845 *ppu8, *pcb, cbRecord));
846
847 if (*ppu8)
848 {
849 Assert (*pcb);
850 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
851 }
852 else
853 {
854 Assert (!*pcb);
855 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
856 }
857
858 if (!pu8New)
859 {
860 /* Memory allocation failed, fail the function. */
861 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
862 cbRecord));
863
864 if (*ppu8)
865 RTMemFree (*ppu8);
866
867 *ppu8 = NULL;
868 *pcb = 0;
869
870 return false;
871 }
872
873 /* Fetch data from the ring buffer. */
874 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
875
876 *ppu8 = pu8New;
877 *pcb = cbRecord;
878
879 return true;
880}
881
882/* For contiguous chunks just return the address in the buffer.
883 * For crossing boundary - allocate a buffer from heap.
884 */
885bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
886{
887 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
888 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
889
890#ifdef DEBUG_sunlover
891 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd:first = %d, free = %d\n",
892 indexRecordFirst, indexRecordFree));
893#endif /* DEBUG_sunlover */
894
895 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
896 {
897 return false;
898 }
899
900 if (indexRecordFirst == indexRecordFree)
901 {
902 /* No records to process. Return without assigning output variables. */
903 return true;
904 }
905
906 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
907
908#ifdef DEBUG_sunlover
909 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: cbRecord = 0x%08X\n",
910 pRecord->cbRecord));
911#endif /* DEBUG_sunlover */
912
913 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
914
915 if (mcbVbvaPartial)
916 {
917 /* There is a partial read in process. Continue with it. */
918
919 Assert (mpu8VbvaPartial);
920
921 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
922 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
923
924 if (cbRecord > mcbVbvaPartial)
925 {
926 /* New data has been added to the record. */
927 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
928 return false;
929 }
930
931 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
932 {
933 /* The record is completed by guest. Return it to the caller. */
934 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
935 *pcbCmd = mcbVbvaPartial;
936
937 mpu8VbvaPartial = NULL;
938 mcbVbvaPartial = 0;
939
940 /* Advance the record index. */
941 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
942
943#ifdef DEBUG_sunlover
944 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: partial done ok, data = %d, free = %d\n",
945 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
946#endif /* DEBUG_sunlover */
947 }
948
949 return true;
950 }
951
952 /* A new record need to be processed. */
953 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
954 {
955 /* Current record is being written by guest. '=' is important here. */
956 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
957 {
958 /* Partial read must be started. */
959 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
960 return false;
961
962 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
963 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
964 }
965
966 return true;
967 }
968
969 /* Current record is complete. */
970
971 /* The size of largest contiguous chunk in the ring biffer. */
972 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
973
974 /* The ring buffer pointer. */
975 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
976
977 /* The pointer to data in the ring buffer. */
978 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
979
980 /* Fetch or point the data. */
981 if (u32BytesTillBoundary >= cbRecord)
982 {
983 /* The command does not cross buffer boundary. Return address in the buffer. */
984 *ppHdr = (VBVACMDHDR *)src;
985
986 /* Advance data offset. */
987 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
988 }
989 else
990 {
991 /* The command crosses buffer boundary. Rare case, so not optimized. */
992 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
993
994 if (!dst)
995 {
996 LogFlow(("MAIN::DisplayImpl::vbvaFetchCmd: could not allocate %d bytes from heap!!!\n", cbRecord));
997 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
998 return false;
999 }
1000
1001 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
1002
1003 *ppHdr = (VBVACMDHDR *)dst;
1004
1005#ifdef DEBUG_sunlover
1006 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: Allocated from heap %p\n", dst));
1007#endif /* DEBUG_sunlover */
1008 }
1009
1010 *pcbCmd = cbRecord;
1011
1012 /* Advance the record index. */
1013 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1014
1015#ifdef DEBUG_sunlover
1016 LogFlow(("MAIN::DisplayImpl::vbvaFetchBytes: done ok, data = %d, free = %d\n",
1017 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1018#endif /* DEBUG_sunlover */
1019
1020 return true;
1021}
1022
1023void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1024{
1025 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1026
1027 if ( (uint8_t *)pHdr >= au8RingBuffer
1028 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1029 {
1030 /* The pointer is inside ring buffer. Must be continuous chunk. */
1031 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1032
1033 /* Do nothing. */
1034
1035 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1036 }
1037 else
1038 {
1039 /* The pointer is outside. It is then an allocated copy. */
1040
1041#ifdef DEBUG_sunlover
1042 LogFlow(("MAIN::DisplayImpl::vbvaReleaseCmd: Free heap %p\n", pHdr));
1043#endif /* DEBUG_sunlover */
1044
1045 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1046 {
1047 mpu8VbvaPartial = NULL;
1048 mcbVbvaPartial = 0;
1049 }
1050 else
1051 {
1052 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1053 }
1054
1055 RTMemFree (pHdr);
1056 }
1057
1058 return;
1059}
1060
1061/**
1062 * Called regularly on the DisplayRefresh timer.
1063 * Also on behalf of guest, when the ring buffer is full.
1064 *
1065 * @thread EMT
1066 */
1067void Display::VideoAccelFlush (void)
1068{
1069#ifdef DEBUG_sunlover
1070 LogFlow(("Display::VideoAccelFlush: mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1071#endif /* DEBUG_sunlover */
1072
1073 if (!mfVideoAccelEnabled)
1074 {
1075 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1076 return;
1077 }
1078
1079 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1080 Assert(mpVbvaMemory);
1081
1082#ifdef DEBUG_sunlover
1083 LogFlow(("Display::VideoAccelFlush: indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1084 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1085#endif /* DEBUG_sunlover */
1086
1087 /* Quick check for "nothing to update" case. */
1088 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1089 return;
1090
1091 /* Process the ring buffer */
1092
1093 bool fFramebufferIsNull = (mFramebuffer == NULL);
1094
1095 if (!fFramebufferIsNull)
1096 mFramebuffer->Lock();
1097
1098 /* Initialize dirty rectangles accumulator. */
1099 VBVADIRTYREGION rgn;
1100 vbvaRgnInit (&rgn, mFramebuffer, this, mpDrv->pUpPort);
1101
1102 for (;;)
1103 {
1104 VBVACMDHDR *phdr = NULL;
1105 uint32_t cbCmd = 0;
1106
1107 /* Fetch the command data. */
1108 if (!vbvaFetchCmd (&phdr, &cbCmd))
1109 {
1110 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1111 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1112
1113 /* Disable VBVA on those processing errors. */
1114 VideoAccelEnable (false, NULL);
1115
1116 break;
1117 }
1118
1119 if (!cbCmd)
1120 {
1121 /* No more commands yet in the queue. */
1122 break;
1123 }
1124
1125 if (!fFramebufferIsNull)
1126 {
1127#ifdef DEBUG_sunlover
1128 LogFlow(("MAIN::DisplayImpl::VideoAccelFlush: hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n", cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1129#endif /* DEBUG_sunlover */
1130
1131 /* Handle the command.
1132 *
1133 * Guest is responsible for updating the guest video memory.
1134 * The Windows guest does all drawing using Eng*.
1135 *
1136 * For local output, only dirty rectangle information is used
1137 * to update changed areas.
1138 *
1139 * Dirty rectangles are accumulated to exclude overlapping updates and
1140 * group small updates to a larger one.
1141 */
1142
1143 /* Accumulate the update. */
1144 vbvaRgnDirtyRect (&rgn, phdr);
1145
1146// /* Forward the command to VRDP server. */
1147// mParent->consoleVRDPServer()->SendUpdate (phdr, cbCmd);
1148 }
1149
1150 vbvaReleaseCmd (phdr, cbCmd);
1151 }
1152
1153 if (!fFramebufferIsNull)
1154 mFramebuffer->Unlock ();
1155
1156 /* Draw the framebuffer. */
1157 vbvaRgnUpdateFramebuffer (&rgn);
1158}
1159
1160/**
1161 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1162 */
1163DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1164{
1165 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1166 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
1167
1168 if (RTUuidCompare2Strs(pszIID, PDMIBASE_IID) == 0)
1169 return &pDrvIns->IBase;
1170 if (RTUuidCompare2Strs(pszIID, PDMIDISPLAYCONNECTOR_IID) == 0)
1171 return &pDrv->Connector;
1172 return NULL;
1173}
1174
1175
1176/**
1177 * Construct a display driver instance.
1178 *
1179 * @copydoc FNPDMDRVCONSTRUCT
1180 */
1181DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1182{
1183 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
1184 LogFlow(("Display::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
1185 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1186
1187 /*
1188 * Validate configuration.
1189 */
1190 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
1191 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1192 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1193 ("Configuration error: Not possible to attach anything to this driver!\n"),
1194 VERR_PDM_DRVINS_NO_ATTACH);
1195
1196 /*
1197 * Init Interfaces.
1198 */
1199 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
1200
1201 pData->Connector.pfnResize = Display::displayResizeCallback;
1202 pData->Connector.pfnUpdateRect = Display::displayUpdateCallback;
1203 pData->Connector.pfnRefresh = Display::displayRefreshCallback;
1204 pData->Connector.pfnReset = Display::displayResetCallback;
1205 pData->Connector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
1206 pData->Connector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
1207 pData->Connector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
1208
1209 /*
1210 * Get the IDisplayPort interface of the above driver/device.
1211 */
1212 pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
1213 if (!pData->pUpPort)
1214 {
1215 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
1216 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1217 }
1218
1219 /*
1220 * Get the Display object pointer and update the mpDrv member.
1221 */
1222 void *pv;
1223 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
1224 if (RT_FAILURE(rc))
1225 {
1226 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
1227 return rc;
1228 }
1229 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
1230 pData->pDisplay->mpDrv = pData;
1231
1232 /*
1233 * If there is a Framebuffer, we have to update our display information
1234 */
1235 if (pData->pDisplay->mFramebuffer)
1236 pData->pDisplay->updateDisplayData();
1237
1238 /*
1239 * Start periodic screen refreshes
1240 */
1241 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 50);
1242
1243 return VINF_SUCCESS;
1244}
1245
1246
1247/**
1248 * Display driver registration record.
1249 */
1250const PDMDRVREG Display::DrvReg =
1251{
1252 /* u32Version */
1253 PDM_DRVREG_VERSION,
1254 /* szName */
1255 "MainDisplay",
1256 /* szRCMod */
1257 "",
1258 /* szR0Mod */
1259 "",
1260 /* pszDescription */
1261 "Main display driver (Main as in the API).",
1262 /* fFlags */
1263 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1264 /* fClass. */
1265 PDM_DRVREG_CLASS_DISPLAY,
1266 /* cMaxInstances */
1267 ~0U,
1268 /* cbInstance */
1269 sizeof(DRVMAINDISPLAY),
1270 /* pfnConstruct */
1271 Display::drvConstruct,
1272 /* pfnDestruct */
1273 NULL,
1274 /* pfnRelocate */
1275 NULL,
1276 /* pfnIOCtl */
1277 NULL,
1278 /* pfnPowerOn */
1279 NULL,
1280 /* pfnReset */
1281 NULL,
1282 /* pfnSuspend */
1283 NULL,
1284 /* pfnResume */
1285 NULL,
1286 /* pfnAttach */
1287 NULL,
1288 /* pfnDetach */
1289 NULL,
1290 /* pfnPowerOff */
1291 NULL,
1292 /* pfnSoftReset */
1293 NULL,
1294 /* u32EndVersion */
1295 PDM_DRVREG_VERSION
1296};
1297
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette