VirtualBox

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

Last change on this file since 5204 was 4512, checked in by vboxsync, 17 years ago

Stubs for display callbacks in VBoxBFE.

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