VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Miniport/wddm/VBoxVideoCm.cpp@ 33252

Last change on this file since 33252 was 32496, checked in by vboxsync, 14 years ago

wddm/3d: multi-swapchain fixes (for win7 & multi-monitor)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/*
2 * Copyright (C) 2010 Oracle Corporation
3 *
4 * This file is part of VirtualBox Open Source Edition (OSE), as
5 * available from http://www.virtualbox.org. This file is free software;
6 * you can redistribute it and/or modify it under the terms of the GNU
7 * General Public License (GPL) as published by the Free Software
8 * Foundation, in version 2 as it comes in the "COPYING" file of the
9 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
10 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
11 */
12#include "../VBoxVideo.h"
13#include "../Helper.h"
14
15#include <iprt/asm.h>
16
17typedef struct VBOXVIDEOCM_CMD_DR
18{
19 LIST_ENTRY QueueList;
20 PVBOXVIDEOCM_CTX pContext;
21 uint32_t cbMaxCmdSize;
22 volatile uint32_t cRefs;
23
24 VBOXVIDEOCM_CMD_HDR CmdHdr;
25} VBOXVIDEOCM_CMD_DR, *PVBOXVIDEOCM_CMD_DR;
26
27AssertCompile(VBOXWDDM_ROUNDBOUND(RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr), 8) == RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr));
28
29#define VBOXVIDEOCM_HEADER_SIZE() (VBOXWDDM_ROUNDBOUND(sizeof (VBOXVIDEOCM_CMD_DR), 8))
30#define VBOXVIDEOCM_SIZE_FROMBODYSIZE(_s) (VBOXVIDEOCM_HEADER_SIZE() + (_s))
31//#define VBOXVIDEOCM_SIZE(_t) (VBOXVIDEOCM_SIZE_FROMBODYSIZE(sizeof (_t)))
32#define VBOXVIDEOCM_BODY(_pCmd, _t) ( (_t*)(((uint8_t*)(_pCmd)) + VBOXVIDEOCM_HEADER_SIZE()) )
33#define VBOXVIDEOCM_HEAD(_pCmd) ( (PVBOXVIDEOCM_CMD_DR)(((uint8_t*)(_pCmd)) - VBOXVIDEOCM_HEADER_SIZE()) )
34
35#define VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(_s) ( VBOXVIDEOCM_SIZE_FROMBODYSIZE(_s) - RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, CmdHdr))
36
37//#define VBOXVIDEOCM_BODY_FIELD_OFFSET(_ot, _t, _f) ( (_ot)( VBOXVIDEOCM_BODY(0, uint8_t) + RT_OFFSETOF(_t, _f) ) )
38
39typedef struct VBOXVIDEOCM_SESSION
40{
41 /* contexts in this session */
42 LIST_ENTRY QueueEntry;
43 /* contexts in this session */
44 LIST_ENTRY ContextList;
45 /* commands list */
46 LIST_ENTRY CommandsList;
47 /* event used to notify UMD about penging commands */
48 PKEVENT pUmEvent;
49 /* synch lock */
50 FAST_MUTEX Mutex;
51 /* indicates whether event signaling is needed on cmd add */
52 bool bEventNeeded;
53} VBOXVIDEOCM_SESSION, *PVBOXVIDEOCM_SESSION;
54
55#define VBOXCMENTRY_2_CMD(_pE) ((PVBOXVIDEOCM_CMD_DR)((uint8_t*)(_pE) - RT_OFFSETOF(VBOXVIDEOCM_CMD_DR, QueueList)))
56
57void* vboxVideoCmCmdReinitForContext(void *pvCmd, PVBOXVIDEOCM_CTX pContext)
58{
59 PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
60 pHdr->pContext = pContext;
61 pHdr->CmdHdr.u64UmData = pContext->u64UmData;
62 return pvCmd;
63}
64
65void* vboxVideoCmCmdCreate(PVBOXVIDEOCM_CTX pContext, uint32_t cbSize)
66{
67 Assert(cbSize);
68 if (!cbSize)
69 return NULL;
70
71 Assert(VBOXWDDM_ROUNDBOUND(cbSize, 8) == cbSize);
72 cbSize = VBOXWDDM_ROUNDBOUND(cbSize, 8);
73
74 Assert(pContext->pSession);
75 if (!pContext->pSession)
76 return NULL;
77
78 uint32_t cbCmd = VBOXVIDEOCM_SIZE_FROMBODYSIZE(cbSize);
79 PVBOXVIDEOCM_CMD_DR pCmd = (PVBOXVIDEOCM_CMD_DR)vboxWddmMemAllocZero(cbCmd);
80 Assert(pCmd);
81 if (pCmd)
82 {
83 InitializeListHead(&pCmd->QueueList);
84 pCmd->pContext = pContext;
85 pCmd->cbMaxCmdSize = VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(cbSize);
86 pCmd->cRefs = 1;
87 pCmd->CmdHdr.u64UmData = pContext->u64UmData;
88 pCmd->CmdHdr.cbCmd = pCmd->cbMaxCmdSize;
89 }
90 return VBOXVIDEOCM_BODY(pCmd, void);
91}
92
93DECLINLINE(void) vboxVideoCmCmdRetainByHdr(PVBOXVIDEOCM_CMD_DR pHdr)
94{
95 ASMAtomicIncU32(&pHdr->cRefs);
96}
97
98DECLINLINE(void) vboxVideoCmCmdReleaseByHdr(PVBOXVIDEOCM_CMD_DR pHdr)
99{
100 uint32_t cRefs = ASMAtomicDecU32(&pHdr->cRefs);
101 Assert(cRefs < UINT32_MAX/2);
102 if (!cRefs)
103 vboxWddmMemFree(pHdr);
104}
105
106static void vboxVideoCmCmdCancel(PVBOXVIDEOCM_CMD_DR pHdr)
107{
108 InitializeListHead(&pHdr->QueueList);
109 vboxVideoCmCmdReleaseByHdr(pHdr);
110}
111
112static void vboxVideoCmCmdPostByHdr(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CMD_DR pHdr, uint32_t cbSize)
113{
114 bool bSignalEvent = false;
115 if (cbSize != VBOXVIDEOCM_SUBMITSIZE_DEFAULT)
116 {
117 cbSize = VBOXVIDEOCM_SENDSIZE_FROMBODYSIZE(cbSize);
118 Assert(cbSize <= pHdr->cbMaxCmdSize);
119 pHdr->CmdHdr.cbCmd = cbSize;
120 }
121
122 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
123 ExAcquireFastMutex(&pSession->Mutex);
124
125 InsertHeadList(&pSession->CommandsList, &pHdr->QueueList);
126 if (pSession->bEventNeeded)
127 {
128 pSession->bEventNeeded = false;
129 bSignalEvent = true;
130 }
131
132 ExReleaseFastMutex(&pSession->Mutex);
133
134 if (bSignalEvent)
135 KeSetEvent(pSession->pUmEvent, 0, FALSE);
136}
137
138void vboxVideoCmCmdRetain(void *pvCmd)
139{
140 PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
141 vboxVideoCmCmdRetainByHdr(pHdr);
142}
143
144void vboxVideoCmCmdRelease(void *pvCmd)
145{
146 PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
147 vboxVideoCmCmdReleaseByHdr(pHdr);
148}
149
150/**
151 * @param pvCmd memory byffer returned by vboxVideoCmCmdCreate
152 * @param cbSize should be <= cbSize posted to vboxVideoCmCmdCreate on command creation
153 */
154void vboxVideoCmCmdSubmit(void *pvCmd, uint32_t cbSize)
155{
156 PVBOXVIDEOCM_CMD_DR pHdr = VBOXVIDEOCM_HEAD(pvCmd);
157 vboxVideoCmCmdPostByHdr(pHdr->pContext->pSession, pHdr, cbSize);
158}
159
160NTSTATUS vboxVideoCmCmdVisit(PVBOXVIDEOCM_CTX pContext, BOOL bEntireSession, PFNVBOXVIDEOCMCMDVISITOR pfnVisitor, PVOID pvVisitor)
161{
162 PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
163 PLIST_ENTRY pCurEntry = NULL;
164 PVBOXVIDEOCM_CMD_DR pHdr;
165
166 ExAcquireFastMutex(&pSession->Mutex);
167
168 pCurEntry = pSession->CommandsList.Blink;
169 do
170 {
171 if (pCurEntry != &pSession->CommandsList)
172 {
173 pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
174 pCurEntry = pHdr->QueueList.Blink;
175 if (bEntireSession || pHdr->pContext == pContext)
176 {
177 void * pvBody = VBOXVIDEOCM_BODY(pHdr, void);
178 UINT fRet = pfnVisitor(pHdr->pContext, pvBody, pHdr->CmdHdr.cbCmd, pvVisitor);
179 if (fRet & VBOXVIDEOCMCMDVISITOR_RETURN_RMCMD)
180 {
181 RemoveEntryList(&pHdr->QueueList);
182 }
183 if (!(fRet & VBOXVIDEOCMCMDVISITOR_RETURN_CONTINUE))
184 break;
185 }
186 }
187 else
188 {
189 break;
190 }
191 } while (1);
192
193
194 ExReleaseFastMutex(&pSession->Mutex);
195
196 return STATUS_SUCCESS;
197}
198
199void vboxVideoCmCtxInitEmpty(PVBOXVIDEOCM_CTX pContext)
200{
201 InitializeListHead(&pContext->SessionEntry);
202 pContext->pSession = NULL;
203 pContext->u64UmData = 0ULL;
204}
205
206static void vboxVideoCmSessionCtxAddLocked(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
207{
208 InsertHeadList(&pSession->ContextList, &pContext->SessionEntry);
209 pContext->pSession = pSession;
210}
211
212void vboxVideoCmSessionCtxAdd(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
213{
214 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
215 ExAcquireFastMutex(&pSession->Mutex);
216 vboxVideoCmSessionCtxAddLocked(pSession, pContext);
217 ExReleaseFastMutex(&pSession->Mutex);
218
219}
220
221static void vboxVideoCmSessionDestroy(PVBOXVIDEOCM_SESSION pSession)
222{
223 ObDereferenceObject(pSession->pUmEvent);
224 Assert(IsListEmpty(&pSession->ContextList));
225 Assert(IsListEmpty(&pSession->CommandsList));
226 RemoveEntryList(&pSession->QueueEntry);
227 vboxWddmMemFree(pSession);
228}
229
230/**
231 * @return true iff the given session is destroyed
232 */
233bool vboxVideoCmSessionCtxRemove(PVBOXVIDEOCM_SESSION pSession, PVBOXVIDEOCM_CTX pContext)
234{
235 bool bDestroy;
236 LIST_ENTRY RemainedList;
237 LIST_ENTRY *pCur;
238 LIST_ENTRY *pPrev;
239 InitializeListHead(&RemainedList);
240 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
241 ExAcquireFastMutex(&pSession->Mutex);
242 pContext->pSession = NULL;
243 RemoveEntryList(&pContext->SessionEntry);
244 bDestroy = !!(IsListEmpty(&pSession->ContextList));
245 /* ensure there are no commands left for the given context */
246 if (bDestroy)
247 {
248 vboxVideoLeDetach(&pSession->CommandsList, &RemainedList);
249 }
250 else
251 {
252 pCur = pSession->CommandsList.Flink;
253 pPrev = &pSession->CommandsList;
254 while (pCur != &pSession->CommandsList)
255 {
256 PVBOXVIDEOCM_CMD_DR pCmd = VBOXCMENTRY_2_CMD(pCur);
257 if (pCmd->pContext == pContext)
258 {
259 RemoveEntryList(pCur);
260 InsertHeadList(&RemainedList, pCur);
261 pCur = pPrev;
262 /* pPrev - remains unchanged */
263 }
264 else
265 {
266 pPrev = pCur;
267 }
268 pCur = pCur->Flink;
269 }
270 }
271 ExReleaseFastMutex(&pSession->Mutex);
272
273 for (pCur = RemainedList.Flink; pCur != &RemainedList; pCur = RemainedList.Flink)
274 {
275 RemoveEntryList(pCur);
276 PVBOXVIDEOCM_CMD_DR pCmd = VBOXCMENTRY_2_CMD(pCur);
277 vboxVideoCmCmdCancel(pCmd);
278 }
279
280 if (bDestroy)
281 {
282 vboxVideoCmSessionDestroy(pSession);
283 }
284
285 return bDestroy;
286}
287
288/* the session gets destroyed once the last context is removed from it */
289NTSTATUS vboxVideoCmSessionCreate(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_SESSION *ppSession, PKEVENT pUmEvent, PVBOXVIDEOCM_CTX pContext)
290{
291 PVBOXVIDEOCM_SESSION pSession = (PVBOXVIDEOCM_SESSION)vboxWddmMemAllocZero(sizeof (VBOXVIDEOCM_SESSION));
292 Assert(pSession);
293 if (pSession)
294 {
295 InitializeListHead(&pSession->ContextList);
296 InitializeListHead(&pSession->CommandsList);
297 pSession->pUmEvent = pUmEvent;
298 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
299 ExInitializeFastMutex(&pSession->Mutex);
300 pSession->bEventNeeded = true;
301 vboxVideoCmSessionCtxAddLocked(pSession, pContext);
302 InsertHeadList(&pMgr->SessionList, &pSession->QueueEntry);
303 *ppSession = pSession;
304 return STATUS_SUCCESS;
305 }
306 return STATUS_NO_MEMORY;
307}
308
309#define VBOXCMENTRY_2_SESSION(_pE) ((PVBOXVIDEOCM_SESSION)((uint8_t*)(_pE) - RT_OFFSETOF(VBOXVIDEOCM_SESSION, QueueEntry)))
310
311NTSTATUS vboxVideoCmCtxAdd(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_CTX pContext, HANDLE hUmEvent, uint64_t u64UmData)
312{
313 PKEVENT pUmEvent = NULL;
314 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
315 NTSTATUS Status = ObReferenceObjectByHandle(hUmEvent, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode,
316 (PVOID*)&pUmEvent,
317 NULL);
318 Assert(Status == STATUS_SUCCESS);
319 if (Status == STATUS_SUCCESS)
320 {
321 Status = KeWaitForSingleObject(&pMgr->SynchEvent, Executive, KernelMode,
322 FALSE, /* BOOLEAN Alertable */
323 NULL /* PLARGE_INTEGER Timeout */
324 );
325 Assert(Status == STATUS_SUCCESS);
326 if (Status == STATUS_SUCCESS)
327 {
328 bool bFound = false;
329 PVBOXVIDEOCM_SESSION pSession = NULL;
330 for (PLIST_ENTRY pEntry = pMgr->SessionList.Flink; pEntry != &pMgr->SessionList; pEntry = pEntry->Flink)
331 {
332 pSession = VBOXCMENTRY_2_SESSION(pEntry);
333 if (pSession->pUmEvent == pUmEvent)
334 {
335 bFound = true;
336 break;
337 }
338 }
339
340 pContext->u64UmData = u64UmData;
341
342 if (!bFound)
343 {
344 Status = vboxVideoCmSessionCreate(pMgr, &pSession, pUmEvent, pContext);
345 Assert(Status == STATUS_SUCCESS);
346 }
347 else
348 {
349 /* Status = */vboxVideoCmSessionCtxAdd(pSession, pContext);
350 /*Assert(Status == STATUS_SUCCESS);*/
351 }
352 LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
353 Assert(!tstL);
354
355 if (Status == STATUS_SUCCESS)
356 {
357 return STATUS_SUCCESS;
358 }
359 }
360
361 ObDereferenceObject(pUmEvent);
362 }
363 return Status;
364}
365
366NTSTATUS vboxVideoCmCtxRemove(PVBOXVIDEOCM_MGR pMgr, PVBOXVIDEOCM_CTX pContext)
367{
368 PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
369 if (!pSession)
370 return STATUS_SUCCESS;
371
372 NTSTATUS Status = KeWaitForSingleObject(&pMgr->SynchEvent, Executive, KernelMode,
373 FALSE, /* BOOLEAN Alertable */
374 NULL /* PLARGE_INTEGER Timeout */
375 );
376 Assert(Status == STATUS_SUCCESS);
377 if (Status == STATUS_SUCCESS)
378 {
379 vboxVideoCmSessionCtxRemove(pSession, pContext);
380 LONG tstL = KeSetEvent(&pMgr->SynchEvent, 0, FALSE);
381 Assert(!tstL);
382 }
383
384 return Status;
385}
386
387NTSTATUS vboxVideoCmInit(PVBOXVIDEOCM_MGR pMgr)
388{
389 KeInitializeEvent(&pMgr->SynchEvent, SynchronizationEvent, TRUE);
390 InitializeListHead(&pMgr->SessionList);
391 return STATUS_SUCCESS;
392}
393
394NTSTATUS vboxVideoCmTerm(PVBOXVIDEOCM_MGR pMgr)
395{
396 Assert(IsListEmpty(&pMgr->SessionList));
397 return STATUS_SUCCESS;
398}
399
400NTSTATUS vboxVideoCmEscape(PVBOXVIDEOCM_CTX pContext, PVBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD pCmd, uint32_t cbCmd)
401{
402 Assert(cbCmd >= sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD));
403 if (cbCmd < sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD))
404 return STATUS_BUFFER_TOO_SMALL;
405
406 PVBOXVIDEOCM_SESSION pSession = pContext->pSession;
407 PVBOXVIDEOCM_CMD_DR pHdr;
408 LIST_ENTRY DetachedList;
409 PLIST_ENTRY pCurEntry = NULL;
410 uint32_t cbCmdsReturned = 0;
411 uint32_t cbRemainingCmds = 0;
412 uint32_t cbRemainingFirstCmd = 0;
413 uint32_t cbData = cbCmd - sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD);
414 uint8_t * pvData = ((uint8_t *)pCmd) + sizeof (VBOXDISPIFESCAPE_GETVBOXVIDEOCMCMD);
415 bool bDetachMode = true;
416 InitializeListHead(&DetachedList);
417// PVBOXWDDM_GETVBOXVIDEOCMCMD_HDR *pvCmd
418
419 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
420 ExAcquireFastMutex(&pSession->Mutex);
421
422 do
423 {
424 if (bDetachMode)
425 {
426 if (!IsListEmpty(&pSession->CommandsList))
427 {
428 Assert(!pCurEntry);
429 pHdr = VBOXCMENTRY_2_CMD(pSession->CommandsList.Blink);
430 Assert(pHdr->CmdHdr.cbCmd);
431 if (cbData >= pHdr->CmdHdr.cbCmd)
432 {
433 RemoveEntryList(&pHdr->QueueList);
434 InsertHeadList(&DetachedList, &pHdr->QueueList);
435 cbData -= pHdr->CmdHdr.cbCmd;
436 }
437 else
438 {
439 cbRemainingFirstCmd = pHdr->CmdHdr.cbCmd;
440 cbRemainingCmds = pHdr->CmdHdr.cbCmd;
441 pCurEntry = pHdr->QueueList.Blink;
442 bDetachMode = false;
443 }
444 }
445 else
446 {
447 pSession->bEventNeeded = true;
448 break;
449 }
450 }
451 else
452 {
453 Assert(pCurEntry);
454 if (pCurEntry != &pSession->CommandsList)
455 {
456 pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
457 Assert(cbRemainingFirstCmd);
458 cbRemainingCmds += pHdr->CmdHdr.cbCmd;
459 pCurEntry = pHdr->QueueList.Blink;
460 }
461 else
462 {
463 pSession->bEventNeeded = false;
464 break;
465 }
466 }
467 } while (1);
468
469 ExReleaseFastMutex(&pSession->Mutex);
470
471 pCmd->Hdr.cbCmdsReturned = 0;
472 for (pCurEntry = DetachedList.Blink; pCurEntry != &DetachedList; pCurEntry = DetachedList.Blink)
473 {
474 pHdr = VBOXCMENTRY_2_CMD(pCurEntry);
475 memcpy(pvData, &pHdr->CmdHdr, pHdr->CmdHdr.cbCmd);
476 pvData += pHdr->CmdHdr.cbCmd;
477 pCmd->Hdr.cbCmdsReturned += pHdr->CmdHdr.cbCmd;
478 RemoveEntryList(pCurEntry);
479 vboxVideoCmCmdReleaseByHdr(pHdr);
480 }
481
482 pCmd->Hdr.cbRemainingCmds = cbRemainingCmds;
483 pCmd->Hdr.cbRemainingFirstCmd = cbRemainingFirstCmd;
484 pCmd->Hdr.u32Reserved = 0;
485
486 return STATUS_SUCCESS;
487}
488
489VOID vboxVideoCmLock(PVBOXVIDEOCM_CTX pContext)
490{
491 ExAcquireFastMutex(&pContext->pSession->Mutex);
492}
493
494VOID vboxVideoCmUnlock(PVBOXVIDEOCM_CTX pContext)
495{
496 ExReleaseFastMutex(&pContext->pSession->Mutex);
497}
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