VirtualBox

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

Last change on this file since 170 was 146, checked in by vboxsync, 18 years ago

64-bit.

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

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