VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DisplayImplLegacy.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
RevLine 
[13607]1/* $Id: DisplayImplLegacy.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
[1]2/** @file
[67914]3 * VirtualBox IDisplay implementation, helpers for legacy GAs.
[52769]4 *
[84564]5 * Methods and helpers to support old Guest Additions 3.x or older.
6 * This is not used by the current Guest Additions.
[1]7 */
8
9/*
[98103]10 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]11 *
[96407]12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * SPDX-License-Identifier: GPL-3.0-only
[1]29 */
30
[67914]31#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
32#include "LoggingNew.h"
33
[1]34#include "DisplayImpl.h"
35#include "ConsoleImpl.h"
36#include "ConsoleVRDPServer.h"
37#include "VMMDev.h"
[76394]38#include <VBox/VMMDev.h>
[1]39
[35157]40/* generated header */
41#include "VBoxEvents.h"
42
[1]43
[52769]44int videoAccelConstruct(VIDEOACCEL *pVideoAccel)
[50313]45{
[52769]46 pVideoAccel->pVbvaMemory = NULL;
47 pVideoAccel->fVideoAccelEnabled = false;
[50313]48
[52769]49 pVideoAccel->pu8VbvaPartial = NULL;
50 pVideoAccel->cbVbvaPartial = 0;
[1]51
[52769]52 pVideoAccel->hXRoadsVideoAccel = NIL_RTSEMXROADS;
[94938]53 int vrc = RTSemXRoadsCreate(&pVideoAccel->hXRoadsVideoAccel);
54 AssertRC(vrc);
[46523]55
[94938]56 return vrc;
[24405]57}
58
[52769]59void videoAccelDestroy(VIDEOACCEL *pVideoAccel)
[50405]60{
[52769]61 RTSemXRoadsDestroy(pVideoAccel->hXRoadsVideoAccel);
62 RT_ZERO(*pVideoAccel);
[50405]63}
64
[52769]65static unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
[24405]66{
[63244]67 RT_NOREF(pw, ph);
68
[3153]69 DISPLAYFBINFO *pInfo = pInfos;
70 unsigned uScreenId;
[55988]71 Log9(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
[3153]72 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
73 {
[55988]74 Log9((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
[3153]75 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
76 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
77 {
78 /* The rectangle belongs to the screen. Correct coordinates. */
79 *px -= pInfo->xOrigin;
80 *py -= pInfo->yOrigin;
[55988]81 Log9((" -> %d,%d", *px, *py));
[3153]82 break;
83 }
84 }
85 if (uScreenId == cInfos)
86 {
87 /* Map to primary screen. */
88 uScreenId = 0;
89 }
[55988]90 Log9((" scr %d\n", uScreenId));
[3153]91 return uScreenId;
92}
93
94
[1]95typedef struct _VBVADIRTYREGION
96{
97 /* Copies of object's pointers used by vbvaRgn functions. */
[3153]98 DISPLAYFBINFO *paFramebuffers;
99 unsigned cMonitors;
[1]100 Display *pDisplay;
101 PPDMIDISPLAYPORT pPort;
102
[52769]103 /* The rectangle that includes all dirty rectangles. */
104 RTRECT aDirtyRects[SchemaDefs::MaxGuestMonitors];
105
[1]106} VBVADIRTYREGION;
107
[52064]108static void vbvaRgnInit(VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors,
109 Display *pd, PPDMIDISPLAYPORT pp)
[1]110{
[3153]111 prgn->paFramebuffers = paFramebuffers;
112 prgn->cMonitors = cMonitors;
[1]113 prgn->pDisplay = pd;
114 prgn->pPort = pp;
[7207]115
[52769]116 RT_ZERO(prgn->aDirtyRects);
[1]117}
118
[52064]119static void vbvaRgnDirtyRect(VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
[1]120{
[55988]121 Log9(("x = %d, y = %d, w = %d, h = %d\n", phdr->x, phdr->y, phdr->w, phdr->h));
[1]122
123 /*
124 * Here update rectangles are accumulated to form an update area.
[55988]125 */
126 /** @todo
[33540]127 * Now the simplest method is used which builds one rectangle that
[1]128 * includes all update areas. A bit more advanced method can be
129 * employed here. The method should be fast however.
130 */
131 if (phdr->w == 0 || phdr->h == 0)
132 {
133 /* Empty rectangle. */
134 return;
135 }
136
137 int32_t xRight = phdr->x + phdr->w;
138 int32_t yBottom = phdr->y + phdr->h;
139
[52769]140 RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
[3153]141 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
142
[52769]143 if (pDirtyRect->xRight == 0)
[1]144 {
145 /* This is the first rectangle to be added. */
[52769]146 pDirtyRect->xLeft = phdr->x;
147 pDirtyRect->yTop = phdr->y;
148 pDirtyRect->xRight = xRight;
149 pDirtyRect->yBottom = yBottom;
[1]150 }
151 else
152 {
153 /* Adjust region coordinates. */
[52769]154 if (pDirtyRect->xLeft > phdr->x)
[1]155 {
[52769]156 pDirtyRect->xLeft = phdr->x;
[1]157 }
158
[52769]159 if (pDirtyRect->yTop > phdr->y)
[1]160 {
[52769]161 pDirtyRect->yTop = phdr->y;
[1]162 }
163
[52769]164 if (pDirtyRect->xRight < xRight)
[1]165 {
[52769]166 pDirtyRect->xRight = xRight;
[1]167 }
168
[52769]169 if (pDirtyRect->yBottom < yBottom)
[1]170 {
[52769]171 pDirtyRect->yBottom = yBottom;
[1]172 }
173 }
174
[3153]175 if (pFBInfo->fDefaultFormat)
[2798]176 {
[63563]177 /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
[52064]178 prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
[52769]179 prgn->pDisplay->i_handleDisplayUpdate(uScreenId, phdr->x, phdr->y, phdr->w, phdr->h);
[2798]180 }
181
[1]182 return;
183}
184
[52064]185static void vbvaRgnUpdateFramebuffer(VBVADIRTYREGION *prgn, unsigned uScreenId)
[1]186{
[52769]187 RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
[3153]188 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
[1]189
[52769]190 uint32_t w = pDirtyRect->xRight - pDirtyRect->xLeft;
191 uint32_t h = pDirtyRect->yBottom - pDirtyRect->yTop;
[3153]192
[51513]193 if (!pFBInfo->fDefaultFormat && w != 0 && h != 0)
[1]194 {
[63563]195 /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
[52769]196 prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
197 prgn->pDisplay->i_handleDisplayUpdate(uScreenId, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
[1]198 }
199}
200
[52769]201void i_vbvaSetMemoryFlags(VBVAMEMORY *pVbvaMemory,
202 bool fVideoAccelEnabled,
203 bool fVideoAccelVRDP,
204 uint32_t fu32SupportedOrders,
205 DISPLAYFBINFO *paFBInfos,
206 unsigned cFBInfos)
[1]207{
208 if (pVbvaMemory)
209 {
210 /* This called only on changes in mode. So reset VRDP always. */
211 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
212
213 if (fVideoAccelEnabled)
214 {
215 fu32Flags |= VBVA_F_MODE_ENABLED;
216
217 if (fVideoAccelVRDP)
218 {
219 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
220
221 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
222 }
223 }
224
225 pVbvaMemory->fu32ModeFlags = fu32Flags;
226 }
[3153]227
228 unsigned uScreenId;
229 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
230 {
231 if (paFBInfos[uScreenId].pHostEvents)
232 {
233 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
234 }
235 }
[25069]236}
[25052]237
[52064]238bool Display::i_VideoAccelAllowed(void)
[1]239{
240 return true;
241}
242
[52769]243int videoAccelEnterVGA(VIDEOACCEL *pVideoAccel)
[24924]244{
[52769]245 return RTSemXRoadsNSEnter(pVideoAccel->hXRoadsVideoAccel);
[24924]246}
247
[52769]248void videoAccelLeaveVGA(VIDEOACCEL *pVideoAccel)
[24924]249{
[52769]250 RTSemXRoadsNSLeave(pVideoAccel->hXRoadsVideoAccel);
[24924]251}
[46523]252
[52769]253int videoAccelEnterVMMDev(VIDEOACCEL *pVideoAccel)
[51762]254{
[52769]255 return RTSemXRoadsEWEnter(pVideoAccel->hXRoadsVideoAccel);
[51762]256}
[46523]257
[52769]258void videoAccelLeaveVMMDev(VIDEOACCEL *pVideoAccel)
[52652]259{
[52769]260 RTSemXRoadsEWLeave(pVideoAccel->hXRoadsVideoAccel);
[52652]261}
262
[1]263/**
264 * @thread EMT
265 */
[52769]266int Display::i_VideoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
[1]267{
[52652]268 LogRelFlowFunc(("fEnable = %d\n", fEnable));
[51762]269
[94938]270 int vrc = i_videoAccelEnable(fEnable, pVbvaMemory, pUpPort);
[51762]271
[94938]272 LogRelFlowFunc(("%Rrc.\n", vrc));
273 return vrc;
[24890]274}
275
[52769]276int Display::i_videoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
[24890]277{
[52769]278 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
279
[1]280 /* Called each time the guest wants to use acceleration,
281 * or when the VGA device disables acceleration,
282 * or when restoring the saved state with accel enabled.
283 *
284 * VGA device disables acceleration on each video mode change
285 * and on reset.
286 *
287 * Guest enabled acceleration at will. And it has to enable
288 * acceleration after a mode change.
289 */
[45941]290 LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
[52769]291 pVideoAccel->fVideoAccelEnabled, fEnable, pVbvaMemory));
[1]292
293 /* Strictly check parameters. Callers must not pass anything in the case. */
294 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
295
[52064]296 if (!i_VideoAccelAllowed ())
[1]297 return VERR_NOT_SUPPORTED;
298
299 /* Check that current status is not being changed */
[52769]300 if (pVideoAccel->fVideoAccelEnabled == fEnable)
[94938]301 return VINF_SUCCESS;
[1]302
[52769]303 if (pVideoAccel->fVideoAccelEnabled)
[1]304 {
305 /* Process any pending orders and empty the VBVA ring buffer. */
[52769]306 i_videoAccelFlush (pUpPort);
[1]307 }
308
[52769]309 if (!fEnable && pVideoAccel->pVbvaMemory)
310 pVideoAccel->pVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
[1]311
[52652]312 if (fEnable)
[1]313 {
[52652]314 /* Process any pending VGA device changes, resize. */
[52769]315 pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
[1]316 }
317
[52652]318 /* Protect the videoaccel state transition. */
319 RTCritSectEnter(&mVideoAccelLock);
320
[1]321 if (fEnable)
322 {
323 /* Initialize the hardware memory. */
[52769]324 i_vbvaSetMemoryFlags(pVbvaMemory, true, mfVideoAccelVRDP,
[52064]325 mfu32SupportedOrders, maFramebuffers, mcMonitors);
[52769]326 pVbvaMemory->off32Data = 0;
327 pVbvaMemory->off32Free = 0;
[1]328
[52769]329 memset(pVbvaMemory->aRecords, 0, sizeof(pVbvaMemory->aRecords));
330 pVbvaMemory->indexRecordFirst = 0;
331 pVbvaMemory->indexRecordFree = 0;
[1]332
[52769]333 pVideoAccel->pVbvaMemory = pVbvaMemory;
334 pVideoAccel->fVideoAccelEnabled = true;
335
[1]336 LogRel(("VBVA: Enabled.\n"));
337 }
338 else
339 {
[52769]340 pVideoAccel->pVbvaMemory = NULL;
341 pVideoAccel->fVideoAccelEnabled = false;
[52652]342
[1]343 LogRel(("VBVA: Disabled.\n"));
344 }
345
[52652]346 RTCritSectLeave(&mVideoAccelLock);
[1]347
[52652]348 if (!fEnable)
349 {
[52769]350 pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
[52652]351 }
352
353 /* Notify the VMMDev, which saves VBVA status in the saved state,
354 * and needs to know current status.
355 */
356 VMMDev *pVMMDev = mParent->i_getVMMDev();
357 if (pVMMDev)
358 {
359 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
360 if (pVMMDevPort)
361 pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
362 }
363
[94938]364 LogRelFlowFunc(("VINF_SUCCESS.\n"));
365 return VINF_SUCCESS;
[1]366}
367
[52064]368static bool i_vbvaVerifyRingBuffer(VBVAMEMORY *pVbvaMemory)
[1]369{
[63244]370 RT_NOREF(pVbvaMemory);
[1]371 return true;
372}
373
[52064]374static void i_vbvaFetchBytes(VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
[1]375{
376 if (cbDst >= VBVA_RING_BUFFER_SIZE)
377 {
[52064]378 AssertMsgFailed(("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
[1]379 return;
380 }
381
382 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
383 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
384 int32_t i32Diff = cbDst - u32BytesTillBoundary;
385
386 if (i32Diff <= 0)
387 {
388 /* Chunk will not cross buffer boundary. */
389 memcpy (pu8Dst, src, cbDst);
390 }
391 else
392 {
393 /* Chunk crosses buffer boundary. */
[52064]394 memcpy(pu8Dst, src, u32BytesTillBoundary);
395 memcpy(pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
[1]396 }
397
398 /* Advance data offset. */
399 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
400
401 return;
402}
403
404
[52064]405static bool i_vbvaPartialRead(uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
[1]406{
407 uint8_t *pu8New;
408
409 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
410 *ppu8, *pcb, cbRecord));
411
412 if (*ppu8)
413 {
414 Assert (*pcb);
[52064]415 pu8New = (uint8_t *)RTMemRealloc(*ppu8, cbRecord);
[1]416 }
417 else
418 {
419 Assert (!*pcb);
[52064]420 pu8New = (uint8_t *)RTMemAlloc(cbRecord);
[1]421 }
422
423 if (!pu8New)
424 {
425 /* Memory allocation failed, fail the function. */
426 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
427 cbRecord));
428
429 if (*ppu8)
430 {
[52064]431 RTMemFree(*ppu8);
[1]432 }
433
434 *ppu8 = NULL;
435 *pcb = 0;
436
437 return false;
438 }
439
440 /* Fetch data from the ring buffer. */
[52064]441 i_vbvaFetchBytes(pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
[1]442
443 *ppu8 = pu8New;
444 *pcb = cbRecord;
445
446 return true;
447}
448
449/* For contiguous chunks just return the address in the buffer.
450 * For crossing boundary - allocate a buffer from heap.
451 */
[52769]452static bool i_vbvaFetchCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
[1]453{
[52769]454 VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
[1]455
[52769]456 uint32_t indexRecordFirst = pVbvaMemory->indexRecordFirst;
457 uint32_t indexRecordFree = pVbvaMemory->indexRecordFree;
458
[1]459#ifdef DEBUG_sunlover
[45941]460 LogFlowFunc(("first = %d, free = %d\n",
461 indexRecordFirst, indexRecordFree));
[1]462#endif /* DEBUG_sunlover */
463
[52769]464 if (!i_vbvaVerifyRingBuffer(pVbvaMemory))
[1]465 {
466 return false;
467 }
468
469 if (indexRecordFirst == indexRecordFree)
470 {
471 /* No records to process. Return without assigning output variables. */
472 return true;
473 }
474
[52769]475 uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVbvaMemory->aRecords[indexRecordFirst].cbRecord);
[1]476
477#ifdef DEBUG_sunlover
[52769]478 LogFlowFunc(("cbRecord = 0x%08X\n", cbRecordCurrent));
[1]479#endif /* DEBUG_sunlover */
480
[52769]481 uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
[1]482
[52769]483 if (pVideoAccel->cbVbvaPartial)
[1]484 {
485 /* There is a partial read in process. Continue with it. */
486
[52769]487 Assert(pVideoAccel->pu8VbvaPartial);
[1]488
[52769]489 LogFlowFunc(("continue partial record cbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
490 pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
[1]491
[52769]492 if (cbRecord > pVideoAccel->cbVbvaPartial)
[1]493 {
494 /* New data has been added to the record. */
[52769]495 if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
[1]496 {
497 return false;
498 }
499 }
500
[52769]501 if (!(cbRecordCurrent & VBVA_F_RECORD_PARTIAL))
[1]502 {
503 /* The record is completed by guest. Return it to the caller. */
[52769]504 *ppHdr = (VBVACMDHDR *)pVideoAccel->pu8VbvaPartial;
505 *pcbCmd = pVideoAccel->cbVbvaPartial;
[1]506
[52769]507 pVideoAccel->pu8VbvaPartial = NULL;
508 pVideoAccel->cbVbvaPartial = 0;
[1]509
510 /* Advance the record index. */
[52769]511 pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
[1]512
513#ifdef DEBUG_sunlover
[45941]514 LogFlowFunc(("partial done ok, data = %d, free = %d\n",
[52769]515 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
[1]516#endif /* DEBUG_sunlover */
517 }
518
519 return true;
520 }
521
522 /* A new record need to be processed. */
[52769]523 if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
[1]524 {
525 /* Current record is being written by guest. '=' is important here. */
526 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
527 {
528 /* Partial read must be started. */
[52769]529 if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
[1]530 {
531 return false;
532 }
533
[52769]534 LogFlowFunc(("started partial record cbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
535 pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
[1]536 }
537
538 return true;
539 }
540
541 /* Current record is complete. If it is not empty, process it. */
542 if (cbRecord)
543 {
[33540]544 /* The size of largest contiguous chunk in the ring biffer. */
[52769]545 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
[1]546
547 /* The ring buffer pointer. */
[52769]548 uint8_t *au8RingBuffer = &pVbvaMemory->au8RingBuffer[0];
[1]549
550 /* The pointer to data in the ring buffer. */
[52769]551 uint8_t *src = &au8RingBuffer[pVbvaMemory->off32Data];
[1]552
553 /* Fetch or point the data. */
554 if (u32BytesTillBoundary >= cbRecord)
555 {
556 /* The command does not cross buffer boundary. Return address in the buffer. */
557 *ppHdr = (VBVACMDHDR *)src;
558
559 /* Advance data offset. */
[52769]560 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
[1]561 }
562 else
563 {
564 /* The command crosses buffer boundary. Rare case, so not optimized. */
[52064]565 uint8_t *dst = (uint8_t *)RTMemAlloc(cbRecord);
[1]566
567 if (!dst)
568 {
[45941]569 LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
[52769]570 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
[1]571 return false;
572 }
573
[52769]574 i_vbvaFetchBytes(pVbvaMemory, dst, cbRecord);
[1]575
576 *ppHdr = (VBVACMDHDR *)dst;
577
578#ifdef DEBUG_sunlover
[45941]579 LogFlowFunc(("Allocated from heap %p\n", dst));
[1]580#endif /* DEBUG_sunlover */
581 }
582 }
583
584 *pcbCmd = cbRecord;
585
586 /* Advance the record index. */
[52769]587 pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
[1]588
589#ifdef DEBUG_sunlover
[45941]590 LogFlowFunc(("done ok, data = %d, free = %d\n",
[52769]591 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
[1]592#endif /* DEBUG_sunlover */
593
594 return true;
595}
596
[52769]597static void i_vbvaReleaseCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR *pHdr, int32_t cbCmd)
[1]598{
[63244]599 RT_NOREF(cbCmd);
[52769]600 uint8_t *au8RingBuffer = pVideoAccel->pVbvaMemory->au8RingBuffer;
[1]601
602 if ( (uint8_t *)pHdr >= au8RingBuffer
603 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
604 {
605 /* The pointer is inside ring buffer. Must be continuous chunk. */
[52064]606 Assert(VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
[1]607
608 /* Do nothing. */
609
[52769]610 Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
[1]611 }
612 else
613 {
614 /* The pointer is outside. It is then an allocated copy. */
615
616#ifdef DEBUG_sunlover
[45941]617 LogFlowFunc(("Free heap %p\n", pHdr));
[1]618#endif /* DEBUG_sunlover */
619
[52769]620 if ((uint8_t *)pHdr == pVideoAccel->pu8VbvaPartial)
[1]621 {
[52769]622 pVideoAccel->pu8VbvaPartial = NULL;
623 pVideoAccel->cbVbvaPartial = 0;
[1]624 }
625 else
626 {
[52769]627 Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
[1]628 }
629
[52064]630 RTMemFree(pHdr);
[1]631 }
632
633 return;
634}
635
636
637/**
638 * Called regularly on the DisplayRefresh timer.
639 * Also on behalf of guest, when the ring buffer is full.
640 *
641 * @thread EMT
642 */
[52769]643void Display::i_VideoAccelFlush(PPDMIDISPLAYPORT pUpPort)
[1]644{
[94938]645 int vrc = i_videoAccelFlush(pUpPort);
646 if (RT_FAILURE(vrc))
[51762]647 {
648 /* Disable on errors. */
[52769]649 i_videoAccelEnable(false, NULL, pUpPort);
[51762]650 }
[24890]651}
652
[52769]653int Display::i_videoAccelFlush(PPDMIDISPLAYPORT pUpPort)
[24890]654{
[52769]655 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
656 VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
657
[3681]658#ifdef DEBUG_sunlover_2
[52769]659 LogFlowFunc(("fVideoAccelEnabled = %d\n", pVideoAccel->fVideoAccelEnabled));
[3681]660#endif /* DEBUG_sunlover_2 */
[1]661
[52769]662 if (!pVideoAccel->fVideoAccelEnabled)
[1]663 {
664 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
[51762]665 return VINF_SUCCESS;
[1]666 }
667
668 /* Here VBVA is enabled and we have the accelerator memory pointer. */
[52769]669 Assert(pVbvaMemory);
[1]670
[3681]671#ifdef DEBUG_sunlover_2
[45941]672 LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
[52769]673 pVbvaMemory->indexRecordFirst, pVbvaMemory->indexRecordFree,
674 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
[3681]675#endif /* DEBUG_sunlover_2 */
[1]676
677 /* Quick check for "nothing to update" case. */
[52769]678 if (pVbvaMemory->indexRecordFirst == pVbvaMemory->indexRecordFree)
[1]679 {
[51762]680 return VINF_SUCCESS;
[1]681 }
682
683 /* Process the ring buffer */
[3153]684 unsigned uScreenId;
[1]685
686 /* Initialize dirty rectangles accumulator. */
687 VBVADIRTYREGION rgn;
[52769]688 vbvaRgnInit(&rgn, maFramebuffers, mcMonitors, this, pUpPort);
[1]689
690 for (;;)
691 {
692 VBVACMDHDR *phdr = NULL;
[63147]693 uint32_t cbCmd = UINT32_MAX;
[1]694
695 /* Fetch the command data. */
[52769]696 if (!i_vbvaFetchCmd(pVideoAccel, &phdr, &cbCmd))
[1]697 {
698 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
[52769]699 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
[51762]700 return VERR_INVALID_STATE;
[1]701 }
702
703 if (cbCmd == uint32_t(~0))
704 {
705 /* No more commands yet in the queue. */
[52769]706#ifdef DEBUG_sunlover
707 LogFlowFunc(("no command\n"));
708#endif /* DEBUG_sunlover */
[1]709 break;
710 }
711
[3153]712 if (cbCmd != 0)
[1]713 {
714#ifdef DEBUG_sunlover
[45941]715 LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
716 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
[1]717#endif /* DEBUG_sunlover */
718
[3153]719 VBVACMDHDR hdrSaved = *phdr;
720
721 int x = phdr->x;
722 int y = phdr->y;
723 int w = phdr->w;
724 int h = phdr->h;
725
726 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
727
728 phdr->x = (int16_t)x;
729 phdr->y = (int16_t)y;
730 phdr->w = (uint16_t)w;
731 phdr->h = (uint16_t)h;
[7207]732
[51603]733 /* Handle the command.
734 *
735 * Guest is responsible for updating the guest video memory.
736 * The Windows guest does all drawing using Eng*.
737 *
738 * For local output, only dirty rectangle information is used
739 * to update changed areas.
740 *
741 * Dirty rectangles are accumulated to exclude overlapping updates and
742 * group small updates to a larger one.
743 */
[1]744
[51603]745 /* Accumulate the update. */
[52064]746 vbvaRgnDirtyRect(&rgn, uScreenId, phdr);
[1]747
[51603]748 /* Forward the command to VRDP server. */
[52064]749 mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, phdr, cbCmd);
[7207]750
[51603]751 *phdr = hdrSaved;
[1]752 }
753
[52769]754 i_vbvaReleaseCmd(pVideoAccel, phdr, cbCmd);
[1]755 }
756
[3153]757 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
[1]758 {
[51603]759 /* Draw the framebuffer. */
[52064]760 vbvaRgnUpdateFramebuffer(&rgn, uScreenId);
[2266]761 }
[51762]762 return VINF_SUCCESS;
[1]763}
764
[52769]765int Display::i_videoAccelRefreshProcess(PPDMIDISPLAYPORT pUpPort)
[24924]766{
[94938]767 int vrc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
[1]768
[52769]769 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
[24924]770
[52769]771 videoAccelEnterVGA(pVideoAccel);
772
773 if (pVideoAccel->fVideoAccelEnabled)
[24924]774 {
[52769]775 Assert(pVideoAccel->pVbvaMemory);
[94938]776 vrc = i_videoAccelFlush(pUpPort);
777 if (RT_FAILURE(vrc))
[24924]778 {
[52667]779 /* Disable on errors. */
[52769]780 i_videoAccelEnable(false, NULL, pUpPort);
[94938]781 vrc = VWRN_INVALID_STATE; /* Do a display update in VGA device. */
[24924]782 }
[52667]783 else
[24924]784 {
[94938]785 vrc = VINF_SUCCESS;
[24924]786 }
787 }
788
[52769]789 videoAccelLeaveVGA(pVideoAccel);
[24924]790
[94938]791 return vrc;
[24924]792}
793
[52769]794void Display::processAdapterData(void *pvVRAM, uint32_t u32VRAMSize)
[51525]795{
[63244]796 RT_NOREF(u32VRAMSize);
[3153]797 if (pvVRAM == NULL)
798 {
799 unsigned i;
[52769]800 for (i = 0; i < mcMonitors; i++)
[3153]801 {
[52769]802 DISPLAYFBINFO *pFBInfo = &maFramebuffers[i];
[3153]803
804 pFBInfo->u32Offset = 0;
805 pFBInfo->u32MaxFramebufferSize = 0;
806 pFBInfo->u32InformationSize = 0;
807 }
808 }
[19432]809#ifndef VBOX_WITH_HGSMI
[3153]810 else
811 {
812 uint8_t *pu8 = (uint8_t *)pvVRAM;
813 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
[7207]814
[63563]815 /// @todo
[3153]816 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
817
818 VBOXVIDEOINFOHDR *pHdr;
[7207]819
[3153]820 for (;;)
821 {
822 pHdr = (VBOXVIDEOINFOHDR *)pu8;
[52064]823 pu8 += sizeof(VBOXVIDEOINFOHDR);
[7207]824
[3153]825 if (pu8 >= pu8End)
826 {
827 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
828 break;
829 }
830
831 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
832 {
[52064]833 if (pHdr->u16Length != sizeof(VBOXVIDEOINFODISPLAY))
[3153]834 {
835 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
836 break;
837 }
838
839 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
840
[52769]841 if (pDisplay->u32Index >= mcMonitors)
[3153]842 {
843 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
844 break;
845 }
846
[52769]847 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pDisplay->u32Index];
[3153]848
849 pFBInfo->u32Offset = pDisplay->u32Offset;
850 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
851 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
852
[51092]853 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index,
854 pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
[3153]855 }
[4053]856 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
857 {
[52064]858 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOQUERYCONF32))
[4053]859 {
860 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
861 break;
862 }
863
864 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
865
866 switch (pConf32->u32Index)
867 {
868 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
869 {
[52769]870 pConf32->u32Value = mcMonitors;
[4053]871 } break;
[7207]872
[4053]873 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
874 {
[63563]875 /** @todo make configurable. */
[4053]876 pConf32->u32Value = _1M;
877 } break;
[7207]878
[4053]879 default:
880 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
881 }
882 }
[3153]883 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
884 {
885 if (pHdr->u16Length != 0)
886 {
887 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
888 break;
889 }
890
891 break;
892 }
[51441]893 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP)
[3153]894 {
[51441]895 /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo. cpp pushing this to us? */
[4027]896 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
[3153]897 }
898
899 pu8 += pHdr->u16Length;
900 }
901 }
[19432]902#endif /* !VBOX_WITH_HGSMI */
[3153]903}
904
[52769]905void Display::processDisplayData(void *pvVRAM, unsigned uScreenId)
[3153]906{
[52769]907 if (uScreenId >= mcMonitors)
[3153]908 {
909 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
910 return;
911 }
912
[13606]913 /* Get the display information structure. */
[52769]914 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
[3153]915
916 uint8_t *pu8 = (uint8_t *)pvVRAM;
917 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
[7207]918
[63563]919 /// @todo
[3153]920 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
921
922 VBOXVIDEOINFOHDR *pHdr;
[7207]923
[3153]924 for (;;)
925 {
926 pHdr = (VBOXVIDEOINFOHDR *)pu8;
[52064]927 pu8 += sizeof(VBOXVIDEOINFOHDR);
[7207]928
[3153]929 if (pu8 >= pu8End)
930 {
931 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
932 break;
933 }
934
935 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
936 {
[52064]937 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOSCREEN))
[3153]938 {
939 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
940 break;
941 }
942
943 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
944
945 pFBInfo->xOrigin = pScreen->xOrigin;
946 pFBInfo->yOrigin = pScreen->yOrigin;
947
948 pFBInfo->w = pScreen->u16Width;
949 pFBInfo->h = pScreen->u16Height;
950
[38966]951 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
[51092]952 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width,
953 pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
[3153]954
955 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
956 {
[42861]957 /* Primary screen resize is eeeeeeeee by the VGA device. */
[41492]958 if (pFBInfo->fDisabled)
959 {
960 pFBInfo->fDisabled = false;
[85300]961 ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(), GuestMonitorChangedEventType_Enabled, uScreenId,
962 pFBInfo->xOrigin, pFBInfo->yOrigin, pFBInfo->w, pFBInfo->h);
[41492]963 }
964
[52769]965 i_handleDisplayResize(uScreenId, pScreen->bitsPerPixel,
[85300]966 (uint8_t *)pvVRAM + pFBInfo->u32Offset,
967 pScreen->u32LineSize,
968 pScreen->u16Width, pScreen->u16Height,
969 VBVA_SCREEN_F_ACTIVE,
970 pScreen->xOrigin, pScreen->yOrigin, false);
[3153]971 }
972 }
973 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
974 {
975 if (pHdr->u16Length != 0)
976 {
977 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
978 break;
979 }
980
981 break;
982 }
983 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
984 {
[52064]985 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOHOSTEVENTS))
[3153]986 {
987 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
988 break;
989 }
990
991 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
992
993 pFBInfo->pHostEvents = pHostEvents;
994
995 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
996 pHostEvents));
997 }
998 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
999 {
[52064]1000 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOLINK))
[3153]1001 {
1002 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
1003 break;
1004 }
1005
1006 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
1007 pu8 += pLink->i32Offset;
1008 }
1009 else
1010 {
1011 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
1012 }
1013
1014 pu8 += pHdr->u16Length;
1015 }
1016}
1017
[14772]1018/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use