VirtualBox

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

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

Reset the update event in proper place (similar to r17824, r17825).

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