VirtualBox

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

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

API change for SetVideoModeHint to be able to disable guest screens and to set the origin of guest screens

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