1 /* $Id: VBVABase.cpp $ */
3 * VirtualBox Video driver, common code - VBVA initialisation and helper
8 * Copyright (C) 2006-2016 Oracle Corporation
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 (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 #include <VBox/VBoxVideoGuest.h>
20 #include <VBox/VBoxVideo.h>
23 #include <iprt/assert.h>
24 #include <iprt/string.h>
27 * There is a hardware ring buffer in the graphics device video RAM, formerly
28 * in the VBox VMMDev PCI memory space.
29 * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate.
30 * and vboxHwBufferEndUpdate.
32 * off32Free is writing position. off32Data is reading position.
33 * off32Free == off32Data means buffer is empty.
34 * There must be always gap between off32Data and off32Free when data
36 * Guest only changes off32Free, host changes off32Data.
39 /* Forward declarations of internal functions. */
40 static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx
);
41 static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx
, const void *p
,
42 uint32_t cb
, uint32_t offset
);
43 static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx
,
44 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
45 const void *p
, uint32_t cb
);
48 static bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx
,
49 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
50 int32_t cScreen
, bool bEnable
)
54 #if 0 /* All callers check this */
55 if (ppdev
->bHGSMISupported
)
58 void *p
= VBoxHGSMIBufferAlloc(pHGSMICtx
,
59 sizeof (VBVAENABLE_EX
),
64 LogFunc(("HGSMIHeapAlloc failed\n"));
68 VBVAENABLE_EX
*pEnable
= (VBVAENABLE_EX
*)p
;
70 pEnable
->Base
.u32Flags
= bEnable
? VBVA_F_ENABLE
: VBVA_F_DISABLE
;
71 pEnable
->Base
.u32Offset
= pCtx
->offVRAMBuffer
;
72 pEnable
->Base
.i32Result
= VERR_NOT_SUPPORTED
;
75 pEnable
->Base
.u32Flags
|= VBVA_F_EXTENDED
| VBVA_F_ABSOFFSET
;
76 pEnable
->u32ScreenId
= cScreen
;
79 VBoxHGSMIBufferSubmit(pHGSMICtx
, p
);
83 bRc
= RT_SUCCESS(pEnable
->Base
.i32Result
);
90 VBoxHGSMIBufferFree(pHGSMICtx
, p
);
98 * Public hardware buffer methods.
100 DECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx
,
101 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
102 VBVABUFFER
*pVBVA
, int32_t cScreen
)
106 LogFlowFunc(("pVBVA %p\n", pVBVA
));
108 #if 0 /* All callers check this */
109 if (ppdev
->bHGSMISupported
)
112 LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA
, pCtx
->offVRAMBuffer
));
114 pVBVA
->hostFlags
.u32HostEvents
= 0;
115 pVBVA
->hostFlags
.u32SupportedOrders
= 0;
116 pVBVA
->off32Data
= 0;
117 pVBVA
->off32Free
= 0;
118 memset(pVBVA
->aRecords
, 0, sizeof (pVBVA
->aRecords
));
119 pVBVA
->indexRecordFirst
= 0;
120 pVBVA
->indexRecordFree
= 0;
121 pVBVA
->cbPartialWriteThreshold
= 256;
122 pVBVA
->cbData
= pCtx
->cbBuffer
- sizeof (VBVABUFFER
) + sizeof (pVBVA
->au8Data
);
124 pCtx
->fHwBufferOverflow
= false;
125 pCtx
->pRecord
= NULL
;
128 bRc
= vboxVBVAInformHost(pCtx
, pHGSMICtx
, cScreen
, true);
133 VBoxVBVADisable(pCtx
, pHGSMICtx
, cScreen
);
139 DECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx
,
140 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
145 pCtx
->fHwBufferOverflow
= false;
146 pCtx
->pRecord
= NULL
;
149 vboxVBVAInformHost(pCtx
, pHGSMICtx
, cScreen
, false);
154 DECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx
,
155 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
)
159 // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1));
162 && (pCtx
->pVBVA
->hostFlags
.u32HostEvents
& VBVA_F_MODE_ENABLED
))
164 uint32_t indexRecordNext
;
166 Assert(!pCtx
->fHwBufferOverflow
);
167 Assert(pCtx
->pRecord
== NULL
);
169 indexRecordNext
= (pCtx
->pVBVA
->indexRecordFree
+ 1) % VBVA_MAX_RECORDS
;
171 if (indexRecordNext
== pCtx
->pVBVA
->indexRecordFirst
)
173 /* All slots in the records queue are used. */
174 vboxHwBufferFlush (pHGSMICtx
);
177 if (indexRecordNext
== pCtx
->pVBVA
->indexRecordFirst
)
179 /* Even after flush there is no place. Fail the request. */
180 LogFunc(("no space in the queue of records!!! first %d, last %d\n",
181 pCtx
->pVBVA
->indexRecordFirst
, pCtx
->pVBVA
->indexRecordFree
));
185 /* Initialize the record. */
186 VBVARECORD
*pRecord
= &pCtx
->pVBVA
->aRecords
[pCtx
->pVBVA
->indexRecordFree
];
188 pRecord
->cbRecord
= VBVA_F_RECORD_PARTIAL
;
190 pCtx
->pVBVA
->indexRecordFree
= indexRecordNext
;
192 // LogFunc(("indexRecordNext = %d\n", indexRecordNext));
194 /* Remember which record we are using. */
195 pCtx
->pRecord
= pRecord
;
204 DECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx
)
212 pRecord
= pCtx
->pRecord
;
213 Assert(pRecord
&& (pRecord
->cbRecord
& VBVA_F_RECORD_PARTIAL
));
215 /* Mark the record completed. */
216 pRecord
->cbRecord
&= ~VBVA_F_RECORD_PARTIAL
;
218 pCtx
->fHwBufferOverflow
= false;
219 pCtx
->pRecord
= NULL
;
225 * Private operations.
227 static uint32_t vboxHwBufferAvail (const VBVABUFFER
*pVBVA
)
229 int32_t i32Diff
= pVBVA
->off32Data
- pVBVA
->off32Free
;
231 return i32Diff
> 0? i32Diff
: pVBVA
->cbData
+ i32Diff
;
234 static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx
)
236 /* Issue the flush command. */
237 void *p
= VBoxHGSMIBufferAlloc(pCtx
,
243 LogFunc(("HGSMIHeapAlloc failed\n"));
247 VBVAFLUSH
*pFlush
= (VBVAFLUSH
*)p
;
249 pFlush
->u32Reserved
= 0;
251 VBoxHGSMIBufferSubmit(pCtx
, p
);
253 VBoxHGSMIBufferFree(pCtx
, p
);
259 static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx
, const void *p
,
260 uint32_t cb
, uint32_t offset
)
262 VBVABUFFER
*pVBVA
= pCtx
->pVBVA
;
263 uint32_t u32BytesTillBoundary
= pVBVA
->cbData
- offset
;
264 uint8_t *dst
= &pVBVA
->au8Data
[offset
];
265 int32_t i32Diff
= cb
- u32BytesTillBoundary
;
269 /* Chunk will not cross buffer boundary. */
274 /* Chunk crosses buffer boundary. */
275 memcpy (dst
, p
, u32BytesTillBoundary
);
276 memcpy (&pVBVA
->au8Data
[0], (uint8_t *)p
+ u32BytesTillBoundary
, i32Diff
);
282 static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx
,
283 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
284 const void *p
, uint32_t cb
)
287 uint32_t cbHwBufferAvail
;
289 uint32_t cbWritten
= 0;
291 VBVABUFFER
*pVBVA
= pCtx
->pVBVA
;
294 if (!pVBVA
|| pCtx
->fHwBufferOverflow
)
299 Assert(pVBVA
->indexRecordFirst
!= pVBVA
->indexRecordFree
);
301 pRecord
= pCtx
->pRecord
;
302 Assert(pRecord
&& (pRecord
->cbRecord
& VBVA_F_RECORD_PARTIAL
));
304 // LogFunc(("%d\n", cb));
306 cbHwBufferAvail
= vboxHwBufferAvail (pVBVA
);
310 uint32_t cbChunk
= cb
;
312 // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
313 // pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
315 if (cbChunk
>= cbHwBufferAvail
)
317 LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail
, cbChunk
));
319 vboxHwBufferFlush (pHGSMICtx
);
321 cbHwBufferAvail
= vboxHwBufferAvail (pVBVA
);
323 if (cbChunk
>= cbHwBufferAvail
)
325 LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
326 cb
, cbHwBufferAvail
));
328 if (cbHwBufferAvail
<= pVBVA
->cbPartialWriteThreshold
)
330 LogFunc(("Buffer overflow!!!\n"));
331 pCtx
->fHwBufferOverflow
= true;
336 cbChunk
= cbHwBufferAvail
- pVBVA
->cbPartialWriteThreshold
;
340 Assert(cbChunk
<= cb
);
341 Assert(cbChunk
<= vboxHwBufferAvail (pVBVA
));
343 vboxHwBufferPlaceDataAt (pCtx
, (uint8_t *)p
+ cbWritten
, cbChunk
, pVBVA
->off32Free
);
345 pVBVA
->off32Free
= (pVBVA
->off32Free
+ cbChunk
) % pVBVA
->cbData
;
346 pRecord
->cbRecord
+= cbChunk
;
347 cbHwBufferAvail
-= cbChunk
;
350 cbWritten
+= cbChunk
;
357 * Public writer to the hardware buffer.
359 DECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx
,
360 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx
,
361 const void *pv
, uint32_t cb
)
363 return vboxHwBufferWrite (pCtx
, pHGSMICtx
, pv
, cb
);
366 DECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx
, unsigned code
)
368 VBVABUFFER
*pVBVA
= pCtx
->pVBVA
;
375 if (pVBVA
->hostFlags
.u32SupportedOrders
& (1 << code
))
383 DECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx
,
384 uint32_t offVRAMBuffer
,
387 pCtx
->offVRAMBuffer
= offVRAMBuffer
;
388 pCtx
->cbBuffer
= cbBuffer
;