VirtualBox

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

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

Reset the resize event in proper place. Also in the VBoxBFE.

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