VirtualBox

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

Last change on this file since 1939 was 1550, checked in by vboxsync, 18 years ago

don't include VBoxGuest.h if we don't need to because it drags in ostypes.h and that conflicts with a core darwin type.

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