VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Miniport/wddm/VBoxVideoVdma.cpp@ 30515

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

wddm/3d: visible regions tracking optimizations & fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 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
13#include "../VBoxVideo.h"
14#include "../Helper.h"
15
16#include <VBox/VBoxGuestLib.h>
17#include <VBox/VBoxVideo.h>
18#include "VBoxVideoVdma.h"
19#include "../VBoxVideo.h"
20
21
22NTSTATUS vboxVdmaPipeConstruct(PVBOXVDMAPIPE pPipe)
23{
24 KeInitializeSpinLock(&pPipe->SinchLock);
25 KeInitializeEvent(&pPipe->Event, SynchronizationEvent, FALSE);
26 InitializeListHead(&pPipe->CmdListHead);
27 pPipe->enmState = VBOXVDMAPIPE_STATE_CREATED;
28 pPipe->bNeedNotify = true;
29 return STATUS_SUCCESS;
30}
31
32NTSTATUS vboxVdmaPipeSvrOpen(PVBOXVDMAPIPE pPipe)
33{
34 NTSTATUS Status = STATUS_SUCCESS;
35 KIRQL OldIrql;
36 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
37 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
38 switch (pPipe->enmState)
39 {
40 case VBOXVDMAPIPE_STATE_CREATED:
41 pPipe->enmState = VBOXVDMAPIPE_STATE_OPENNED;
42 pPipe->bNeedNotify = false;
43 break;
44 case VBOXVDMAPIPE_STATE_OPENNED:
45 pPipe->bNeedNotify = false;
46 break;
47 default:
48 AssertBreakpoint();
49 Status = STATUS_INVALID_PIPE_STATE;
50 break;
51 }
52
53 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
54 return Status;
55}
56
57NTSTATUS vboxVdmaPipeSvrClose(PVBOXVDMAPIPE pPipe)
58{
59 NTSTATUS Status = STATUS_SUCCESS;
60 KIRQL OldIrql;
61 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
62 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
63 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
64 switch (pPipe->enmState)
65 {
66 case VBOXVDMAPIPE_STATE_CLOSING:
67 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
68 break;
69 case VBOXVDMAPIPE_STATE_CLOSED:
70 break;
71 default:
72 AssertBreakpoint();
73 Status = STATUS_INVALID_PIPE_STATE;
74 break;
75 }
76
77 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
78 return Status;
79}
80
81NTSTATUS vboxVdmaPipeCltClose(PVBOXVDMAPIPE pPipe)
82{
83 NTSTATUS Status = STATUS_SUCCESS;
84 KIRQL OldIrql;
85 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
86 bool bNeedNotify = false;
87 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
88 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED
89 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
90 switch (pPipe->enmState)
91 {
92 case VBOXVDMAPIPE_STATE_OPENNED:
93 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSING;
94 bNeedNotify = pPipe->bNeedNotify;
95 pPipe->bNeedNotify = false;
96 break;
97 case VBOXVDMAPIPE_STATE_CREATED:
98 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
99 pPipe->bNeedNotify = false;
100 break;
101 case VBOXVDMAPIPE_STATE_CLOSED:
102 break;
103 default:
104 AssertBreakpoint();
105 Status = STATUS_INVALID_PIPE_STATE;
106 break;
107 }
108
109 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
110
111 if (bNeedNotify)
112 {
113 KeSetEvent(&pPipe->Event, 0, FALSE);
114 }
115 return Status;
116}
117
118NTSTATUS vboxVdmaPipeDestruct(PVBOXVDMAPIPE pPipe)
119{
120 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
121 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
122 /* ensure the pipe is closed */
123 NTSTATUS Status = vboxVdmaPipeCltClose(pPipe);
124 Assert(Status == STATUS_SUCCESS);
125
126 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
127
128 return Status;
129}
130
131NTSTATUS vboxVdmaPipeSvrCmdGetList(PVBOXVDMAPIPE pPipe, PLIST_ENTRY pDetachHead)
132{
133 PLIST_ENTRY pEntry = NULL;
134 KIRQL OldIrql;
135 NTSTATUS Status = STATUS_SUCCESS;
136 VBOXVDMAPIPE_STATE enmState = VBOXVDMAPIPE_STATE_CLOSED;
137 do
138 {
139 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
140 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
141 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
142 Assert(pPipe->enmState >= VBOXVDMAPIPE_STATE_OPENNED);
143 enmState = pPipe->enmState;
144 if (enmState >= VBOXVDMAPIPE_STATE_OPENNED)
145 {
146 vboxVideoLeDetach(&pPipe->CmdListHead, pDetachHead);
147 pPipe->bNeedNotify = false;
148 }
149 else
150 {
151 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
152 Status = STATUS_INVALID_PIPE_STATE;
153 break;
154 }
155
156 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
157
158 if (!IsListEmpty(pDetachHead))
159 {
160 Assert(Status == STATUS_SUCCESS);
161 break;
162 }
163
164 if (enmState == VBOXVDMAPIPE_STATE_OPENNED)
165 {
166 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
167 pPipe->bNeedNotify = true;
168 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
169
170 Status = KeWaitForSingleObject(&pPipe->Event, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
171 Assert(Status == STATUS_SUCCESS);
172 if (Status != STATUS_SUCCESS)
173 break;
174 }
175 else
176 {
177 Assert(enmState == VBOXVDMAPIPE_STATE_CLOSING);
178 Status = STATUS_PIPE_CLOSING;
179 break;
180 }
181 } while (1);
182
183 return Status;
184}
185
186NTSTATUS vboxVdmaPipeCltCmdPut(PVBOXVDMAPIPE pPipe, PVBOXVDMAPIPE_CMD_HDR pCmd)
187{
188 NTSTATUS Status = STATUS_SUCCESS;
189 KIRQL OldIrql;
190 bool bNeedNotify = false;
191
192 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
193
194 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED);
195 if (pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED)
196 {
197 bNeedNotify = pPipe->bNeedNotify;
198 InsertHeadList(&pPipe->CmdListHead, &pCmd->ListEntry);
199 pPipe->bNeedNotify = false;
200 }
201 else
202 Status = STATUS_INVALID_PIPE_STATE;
203
204 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
205
206 if (bNeedNotify)
207 {
208 KeSetEvent(&pPipe->Event, 0, FALSE);
209 }
210
211 return Status;
212}
213
214PVBOXVDMAPIPE_CMD_DR vboxVdmaGgCmdCreate(PVBOXVDMAGG pVdma, VBOXVDMAPIPE_CMD_TYPE enmType, uint32_t cbCmd)
215{
216 PVBOXVDMAPIPE_CMD_DR pHdr = (PVBOXVDMAPIPE_CMD_DR)vboxWddmMemAllocZero(cbCmd);
217 Assert(pHdr);
218 if (pHdr)
219 {
220 pHdr->enmType = enmType;
221 return pHdr;
222 }
223 return NULL;
224}
225
226void vboxVdmaGgCmdDestroy(PVBOXVDMAPIPE_CMD_DR pDr)
227{
228 vboxWddmMemFree(pDr);
229}
230
231
232/**
233 * helper function used for system thread creation
234 */
235static NTSTATUS vboxVdmaGgThreadCreate(PKTHREAD * ppThread, PKSTART_ROUTINE pStartRoutine, PVOID pStartContext)
236{
237 NTSTATUS fStatus;
238 HANDLE hThread;
239 OBJECT_ATTRIBUTES fObjectAttributes;
240
241 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
242
243 InitializeObjectAttributes(&fObjectAttributes, NULL, OBJ_KERNEL_HANDLE,
244 NULL, NULL);
245
246 fStatus = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS,
247 &fObjectAttributes, NULL, NULL,
248 (PKSTART_ROUTINE) pStartRoutine, pStartContext);
249 if (!NT_SUCCESS(fStatus))
250 return fStatus;
251
252 ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL,
253 KernelMode, (PVOID*) ppThread, NULL);
254 ZwClose(hThread);
255 return STATUS_SUCCESS;
256}
257
258DECLINLINE(void) vboxVdmaDirtyRectsCalcIntersection(const RECT *pArea, const PVBOXWDDM_RECTS_INFO pRects, PVBOXWDDM_RECTS_INFO pResult)
259{
260 pResult->cRects = 0;
261 for (uint32_t i = 0; i < pRects->cRects; ++i)
262 {
263 if (vboxWddmRectIntersection(pArea, &pRects->aRects[i], &pResult->aRects[pResult->cRects]))
264 {
265 ++pResult->cRects;
266 }
267 }
268}
269
270DECLINLINE(bool) vboxVdmaDirtyRectsHasIntersections(const RECT *paRects1, uint32_t cRects1, const RECT *paRects2, uint32_t cRects2)
271{
272 RECT tmpRect;
273 for (uint32_t i = 0; i < cRects1; ++i)
274 {
275 const RECT * pRect1 = &paRects1[i];
276 for (uint32_t j = 0; j < cRects2; ++j)
277 {
278 const RECT * pRect2 = &paRects2[j];
279 if (vboxWddmRectIntersection(pRect1, pRect2, &tmpRect))
280 return true;
281 }
282 }
283 return false;
284}
285
286DECLINLINE(bool) vboxVdmaDirtyRectsIsCover(const RECT *paRects, uint32_t cRects, const RECT *paRectsCovered, uint32_t cRectsCovered)
287{
288 for (uint32_t i = 0; i < cRectsCovered; ++i)
289 {
290 const RECT * pRectCovered = &paRectsCovered[i];
291 uint32_t j = 0;
292 for (; j < cRects; ++j)
293 {
294 const RECT * pRect = &paRects[j];
295 if (vboxWddmRectIsCoveres(pRect, pRectCovered))
296 break;
297 }
298 if (j == cRects)
299 return false;
300 }
301 return true;
302}
303
304/**
305 * @param pDevExt
306 */
307NTSTATUS vboxVdmaGgDirtyRectsProcess(VBOXVDMAPIPE_CMD_RECTSINFO *pRectsInfo)
308{
309 PVBOXWDDM_CONTEXT pContext = pRectsInfo->pContext;
310 PDEVICE_EXTENSION pDevExt = pContext->pDevice->pAdapter;
311 RECT * pContextRect = &pRectsInfo->ContextsRects.ContextRect;
312 PVBOXWDDM_RECTS_INFO pRects = &pRectsInfo->ContextsRects.UpdateRects;
313 NTSTATUS Status = STATUS_SUCCESS;
314 PVBOXVIDEOCM_CMD_RECTS pCmd = NULL;
315 uint32_t cbCmd = VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pRects->cRects);
316 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
317 ExAcquireFastMutex(&pDevExt->ContextMutex);
318 for (PLIST_ENTRY pCur = pDevExt->ContextList3D.Flink; pCur != &pDevExt->ContextList3D; pCur = pCur->Flink)
319 {
320 if (pCur != &pContext->ListEntry)
321 {
322 PVBOXWDDM_CONTEXT pCurContext = VBOXWDDMENTRY_2_CONTEXT(pCur);
323 if (!pCmd)
324 {
325 pCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pCurContext->CmContext, cbCmd);
326 Assert(pCmd);
327 if (!pCmd)
328 {
329 Status = STATUS_NO_MEMORY;
330 break;
331 }
332 }
333 else
334 {
335 pCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdReinitForContext(pCmd, &pCurContext->CmContext);
336 }
337
338 vboxVdmaDirtyRectsCalcIntersection(&pCurContext->ViewRect, pRects, &pCmd->RectsInfo);
339 if (pCmd->RectsInfo.cRects)
340 {
341 bool bSend = false;
342 Assert(pCmd->fFlags.Value == 0);
343 pCmd->fFlags.bAddHiddenRects = 1;
344 if (pContext->pLastReportedRects)
345 {
346 if (pContext->pLastReportedRects->fFlags.bSetVisibleRects)
347 {
348 RECT *paPrevRects;
349 uint32_t cPrevRects;
350 if (pContext->pLastReportedRects->fFlags.bSetViewRect)
351 {
352 paPrevRects = &pContext->pLastReportedRects->RectsInfo.aRects[1];
353 cPrevRects = pContext->pLastReportedRects->RectsInfo.cRects - 1;
354 }
355 else
356 {
357 paPrevRects = &pContext->pLastReportedRects->RectsInfo.aRects[0];
358 cPrevRects = pContext->pLastReportedRects->RectsInfo.cRects;
359 }
360
361 if (vboxVdmaDirtyRectsHasIntersections(paPrevRects, cPrevRects, pCmd->RectsInfo.aRects, pCmd->RectsInfo.cRects))
362 {
363 bSend = true;
364 }
365 }
366 else
367 {
368 Assert(pContext->pLastReportedRects->fFlags.bAddHiddenRects);
369 if (!vboxVdmaDirtyRectsIsCover(pContext->pLastReportedRects->RectsInfo.aRects,
370 pContext->pLastReportedRects->RectsInfo.cRects,
371 pCmd->RectsInfo.aRects, pCmd->RectsInfo.cRects))
372 {
373 bSend = true;
374 }
375 }
376 }
377 else
378 bSend = true;
379
380 if (bSend)
381 {
382 if (pContext->pLastReportedRects)
383 vboxVideoCmCmdRelease(pContext->pLastReportedRects);
384 vboxVideoCmCmdRetain(pCmd);
385 pContext->pLastReportedRects = pCmd;
386 vboxVideoCmCmdSubmit(pCmd, VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pCmd->RectsInfo.cRects));
387 pCmd = NULL;
388 }
389 }
390 }
391 else
392 {
393 bool bRectShanged = (pContext->ViewRect.left != pContextRect->left
394 || pContext->ViewRect.top != pContextRect->top
395 || pContext->ViewRect.right != pContextRect->right
396 || pContext->ViewRect.bottom != pContextRect->bottom);
397 PVBOXVIDEOCM_CMD_RECTS pDrCmd;
398
399 if (bRectShanged)
400 {
401 uint32_t cbDrCmd = VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pRects->cRects + 1);
402 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pContext->CmContext, cbDrCmd);
403 Assert(pDrCmd);
404 if (!pDrCmd)
405 {
406 Status = STATUS_NO_MEMORY;
407 break;
408 }
409 pDrCmd->RectsInfo.cRects = pRects->cRects + 1;
410 }
411 else
412 {
413 if (pCmd)
414 {
415 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdReinitForContext(pCmd, &pContext->CmContext);
416 pCmd = NULL;
417 }
418 else
419 {
420 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pContext->CmContext, cbCmd);
421 Assert(pDrCmd);
422 if (!pDrCmd)
423 {
424 Status = STATUS_NO_MEMORY;
425 break;
426 }
427 }
428 pDrCmd->RectsInfo.cRects = pRects->cRects;
429 }
430
431 Assert(pDrCmd->fFlags.Value == 0);
432 RECT *pDirtyRect;
433 bool bSend = false;
434 if (bRectShanged)
435 {
436 pDrCmd->fFlags.bSetViewRect = 1;
437 pDrCmd->RectsInfo.aRects[0] = *pContextRect;
438 pDirtyRect = &pDrCmd->RectsInfo.aRects[1];
439 pContext->ViewRect = *pContextRect;
440 bSend = true;
441 }
442 else
443 {
444 pDirtyRect = pDrCmd->RectsInfo.aRects;
445
446 if (pContext->pLastReportedRects)
447 {
448 if (pContext->pLastReportedRects->fFlags.bSetVisibleRects)
449 {
450 RECT *paRects;
451 uint32_t cRects;
452 if (pContext->pLastReportedRects->fFlags.bSetViewRect)
453 {
454 paRects = &pContext->pLastReportedRects->RectsInfo.aRects[1];
455 cRects = pContext->pLastReportedRects->RectsInfo.cRects - 1;
456 }
457 else
458 {
459 paRects = &pContext->pLastReportedRects->RectsInfo.aRects[0];
460 cRects = pContext->pLastReportedRects->RectsInfo.cRects;
461 }
462 bSend = (pDrCmd->RectsInfo.cRects == cRects)
463 && !memcmp(paRects, pDrCmd->RectsInfo.aRects, cRects * sizeof (RECT));
464 }
465 else
466 {
467 Assert(pContext->pLastReportedRects->fFlags.bAddHiddenRects);
468 bSend = true;
469 }
470 }
471 else
472 bSend = true;
473
474 }
475
476 Assert(pRects->cRects);
477 if (bSend)
478 {
479 if (pContext->pLastReportedRects)
480 vboxVideoCmCmdRelease(pContext->pLastReportedRects);
481
482 pDrCmd->fFlags.bSetVisibleRects = 1;
483 memcpy (pDirtyRect, pRects->aRects, sizeof (RECT) * pRects->cRects);
484
485 vboxVideoCmCmdRetain(pDrCmd);
486 pContext->pLastReportedRects = pDrCmd;
487 vboxVideoCmCmdSubmit(pDrCmd, VBOXVIDEOCM_SUBMITSIZE_DEFAULT);
488 }
489 else
490 {
491 if (!pCmd)
492 pCmd = pDrCmd;
493 else
494 vboxVideoCmCmdRelease(pDrCmd);
495 }
496 }
497 }
498 ExReleaseFastMutex(&pDevExt->ContextMutex);
499
500
501 if (pCmd)
502 vboxVideoCmCmdRelease(pCmd);
503
504 return Status;
505}
506
507
508static VOID vboxVdmaGgWorkerThread(PVOID pvUser)
509{
510 PVBOXVDMAGG pVdma = (PVBOXVDMAGG)pvUser;
511
512 NTSTATUS Status = vboxVdmaPipeSvrOpen(&pVdma->CmdPipe);
513 Assert(Status == STATUS_SUCCESS);
514 if (Status == STATUS_SUCCESS)
515 {
516 do
517 {
518 LIST_ENTRY CmdList;
519 Status = vboxVdmaPipeSvrCmdGetList(&pVdma->CmdPipe, &CmdList);
520 Assert(Status == STATUS_SUCCESS || Status == STATUS_PIPE_CLOSING);
521 if (Status == STATUS_SUCCESS)
522 {
523 for (PLIST_ENTRY pCur = CmdList.Blink; pCur != &CmdList; pCur = CmdList.Blink)
524 {
525 PVBOXVDMAPIPE_CMD_DR pDr = VBOXVDMAPIPE_CMD_DR_FROM_ENTRY(pCur);
526 switch (pDr->enmType)
527 {
528 case VBOXVDMAPIPE_CMD_TYPE_RECTSINFO:
529 {
530 PVBOXVDMAPIPE_CMD_RECTSINFO pRects = (PVBOXVDMAPIPE_CMD_RECTSINFO)pDr;
531 Status = vboxVdmaGgDirtyRectsProcess(pRects);
532 Assert(Status == STATUS_SUCCESS);
533 break;
534 }
535 case VBOXVDMAPIPE_CMD_TYPE_DMACMD:
536 default:
537 AssertBreakpoint();
538 }
539 RemoveEntryList(pCur);
540 vboxVdmaGgCmdDestroy(pDr);
541 }
542 }
543 else
544 break;
545 } while (1);
546 }
547
548 /* always try to close the pipe to make sure the client side is notified */
549 Status = vboxVdmaPipeSvrClose(&pVdma->CmdPipe);
550 Assert(Status == STATUS_SUCCESS);
551}
552
553NTSTATUS vboxVdmaGgConstruct(PVBOXVDMAGG pVdma)
554{
555 NTSTATUS Status = vboxVdmaPipeConstruct(&pVdma->CmdPipe);
556 Assert(Status == STATUS_SUCCESS);
557 if (Status == STATUS_SUCCESS)
558 {
559 Status = vboxVdmaGgThreadCreate(&pVdma->pThread, vboxVdmaGgWorkerThread, pVdma);
560 Assert(Status == STATUS_SUCCESS);
561 if (Status == STATUS_SUCCESS)
562 return STATUS_SUCCESS;
563
564 NTSTATUS tmpStatus = vboxVdmaPipeDestruct(&pVdma->CmdPipe);
565 Assert(tmpStatus == STATUS_SUCCESS);
566 }
567
568 /* we're here ONLY in case of an error */
569 Assert(Status != STATUS_SUCCESS);
570 return Status;
571}
572
573NTSTATUS vboxVdmaGgDestruct(PVBOXVDMAGG pVdma)
574{
575 /* this informs the server thread that it should complete all current commands and exit */
576 NTSTATUS Status = vboxVdmaPipeCltClose(&pVdma->CmdPipe);
577 Assert(Status == STATUS_SUCCESS);
578 if (Status == STATUS_SUCCESS)
579 {
580 Status = KeWaitForSingleObject(pVdma->pThread, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
581 Assert(Status == STATUS_SUCCESS);
582 if (Status == STATUS_SUCCESS)
583 {
584 Status = vboxVdmaPipeDestruct(&pVdma->CmdPipe);
585 Assert(Status == STATUS_SUCCESS);
586 }
587 }
588
589 return Status;
590}
591
592NTSTATUS vboxVdmaGgCmdSubmit(PVBOXVDMAGG pVdma, PVBOXVDMAPIPE_CMD_DR pCmd)
593{
594 return vboxVdmaPipeCltCmdPut(&pVdma->CmdPipe, &pCmd->PipeHdr);
595}
596
597/* end */
598
599/*
600 * This is currently used by VDMA. It is invisible for Vdma API clients since
601 * Vdma transport may change if we choose to use another (e.g. more light-weight)
602 * transport for DMA commands submission
603 */
604
605#ifdef VBOXVDMA_WITH_VBVA
606static int vboxWddmVdmaSubmitVbva(struct _DEVICE_EXTENSION* pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
607{
608 int rc;
609 if (vboxVbvaBufferBeginUpdate (pDevExt, &pDevExt->u.primary.Vbva))
610 {
611 rc = vboxVbvaReportCmdOffset(pDevExt, &pDevExt->u.primary.Vbva, offDr);
612 vboxVbvaBufferEndUpdate (pDevExt, &pDevExt->u.primary.Vbva);
613 }
614 else
615 {
616 AssertBreakpoint();
617 rc = VERR_INVALID_STATE;
618 }
619 return rc;
620}
621#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitVbva
622#else
623static int vboxWddmVdmaSubmitHgsmi(struct _DEVICE_EXTENSION* pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
624{
625 VBoxHGSMIGuestWrite(pDevExt, offDr);
626 return VINF_SUCCESS;
627}
628#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitHgsmi
629#endif
630
631static int vboxVdmaInformHost (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo, VBOXVDMA_CTL_TYPE enmCtl)
632{
633 int rc = VINF_SUCCESS;
634
635 PVBOXVDMA_CTL pCmd = (PVBOXVDMA_CTL)VBoxSHGSMICommandAlloc(&pDevExt->u.primary.hgsmiAdapterHeap, sizeof (VBOXVDMA_CTL), HGSMI_CH_VBVA, VBVA_VDMA_CTL);
636 if (pCmd)
637 {
638 pCmd->enmCtl = enmCtl;
639 pCmd->u32Offset = pInfo->CmdHeap.area.offBase;
640 pCmd->i32Result = VERR_NOT_SUPPORTED;
641
642 const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pCmd);
643 Assert(pHdr);
644 if (pHdr)
645 {
646 do
647 {
648 HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
649 Assert(offCmd != HGSMIOFFSET_VOID);
650 if (offCmd != HGSMIOFFSET_VOID)
651 {
652 rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
653 AssertRC(rc);
654 if (RT_SUCCESS(rc))
655 {
656 rc = VBoxSHGSMICommandDoneSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
657 AssertRC(rc);
658 if (RT_SUCCESS(rc))
659 {
660 rc = pCmd->i32Result;
661 AssertRC(rc);
662 }
663 break;
664 }
665 }
666 else
667 rc = VERR_INVALID_PARAMETER;
668 /* fail to submit, cancel it */
669 VBoxSHGSMICommandCancelSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
670 } while (0);
671 }
672
673 VBoxSHGSMICommandFree (&pDevExt->u.primary.hgsmiAdapterHeap, pCmd);
674 }
675 else
676 {
677 drprintf((__FUNCTION__": HGSMIHeapAlloc failed\n"));
678 rc = VERR_OUT_OF_RESOURCES;
679 }
680
681 return rc;
682}
683
684/* create a DMACommand buffer */
685int vboxVdmaCreate (PDEVICE_EXTENSION pDevExt, VBOXVDMAINFO *pInfo, ULONG offBuffer, ULONG cbBuffer)
686{
687 Assert((offBuffer & 0xfff) == 0);
688 Assert((cbBuffer & 0xfff) == 0);
689 Assert(offBuffer);
690 Assert(cbBuffer);
691
692 if((offBuffer & 0xfff)
693 || (cbBuffer & 0xfff)
694 || !offBuffer
695 || !cbBuffer)
696 {
697 drprintf((__FUNCTION__": invalid parameters: offBuffer(0x%x), cbBuffer(0x%x)", offBuffer, cbBuffer));
698 return VERR_INVALID_PARAMETER;
699 }
700
701 pInfo->fEnabled = FALSE;
702 PVOID pvBuffer;
703
704 int rc = VBoxMapAdapterMemory (pDevExt,
705 &pvBuffer,
706 offBuffer,
707 cbBuffer);
708 Assert(RT_SUCCESS(rc));
709 if (RT_SUCCESS(rc))
710 {
711 /* Setup a HGSMI heap within the adapter information area. */
712 rc = HGSMIHeapSetup (&pInfo->CmdHeap,
713 pvBuffer,
714 cbBuffer,
715 offBuffer,
716 false /*fOffsetBased*/);
717 Assert(RT_SUCCESS(rc));
718 if(RT_SUCCESS(rc))
719 {
720 NTSTATUS Status = vboxVdmaGgConstruct(&pInfo->DmaGg);
721 Assert(Status == STATUS_SUCCESS);
722 if (Status == STATUS_SUCCESS)
723 return VINF_SUCCESS;
724 rc = VERR_GENERAL_FAILURE;
725 }
726 else
727 drprintf((__FUNCTION__": HGSMIHeapSetup failed rc = 0x%x\n", rc));
728
729 VBoxUnmapAdapterMemory(pDevExt, &pvBuffer, cbBuffer);
730 }
731 else
732 drprintf((__FUNCTION__": VBoxMapAdapterMemory failed rc = 0x%x\n", rc));
733
734 return rc;
735}
736
737int vboxVdmaDisable (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
738{
739 dfprintf((__FUNCTION__"\n"));
740
741 Assert(pInfo->fEnabled);
742 if (!pInfo->fEnabled)
743 return VINF_ALREADY_INITIALIZED;
744
745 /* ensure nothing else is submitted */
746 pInfo->fEnabled = FALSE;
747
748 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_DISABLE);
749 AssertRC(rc);
750 return rc;
751}
752
753int vboxVdmaEnable (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
754{
755 dfprintf((__FUNCTION__"\n"));
756
757 Assert(!pInfo->fEnabled);
758 if (pInfo->fEnabled)
759 return VINF_ALREADY_INITIALIZED;
760
761 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_ENABLE);
762 Assert(RT_SUCCESS(rc));
763 if (RT_SUCCESS(rc))
764 pInfo->fEnabled = TRUE;
765
766 return rc;
767}
768
769int vboxVdmaFlush (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
770{
771 dfprintf((__FUNCTION__"\n"));
772
773 Assert(pInfo->fEnabled);
774 if (!pInfo->fEnabled)
775 return VINF_ALREADY_INITIALIZED;
776
777 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_FLUSH);
778 Assert(RT_SUCCESS(rc));
779
780 return rc;
781}
782
783int vboxVdmaDestroy (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
784{
785 int rc = VINF_SUCCESS;
786 NTSTATUS Status = vboxVdmaGgDestruct(&pInfo->DmaGg);
787 Assert(Status == STATUS_SUCCESS);
788 if (Status == STATUS_SUCCESS)
789 {
790 Assert(!pInfo->fEnabled);
791 if (pInfo->fEnabled)
792 rc = vboxVdmaDisable (pDevExt, pInfo);
793 VBoxUnmapAdapterMemory (pDevExt, (void**)&pInfo->CmdHeap.area.pu8Base, pInfo->CmdHeap.area.cbArea);
794 }
795 else
796 rc = VERR_GENERAL_FAILURE;
797 return rc;
798}
799
800void vboxVdmaCBufDrFree (PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
801{
802 VBoxSHGSMICommandFree (&pInfo->CmdHeap, pDr);
803}
804
805PVBOXVDMACBUF_DR vboxVdmaCBufDrCreate (PVBOXVDMAINFO pInfo, uint32_t cbTrailingData)
806{
807 uint32_t cbDr = VBOXVDMACBUF_DR_SIZE(cbTrailingData);
808 PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)VBoxSHGSMICommandAlloc (&pInfo->CmdHeap, cbDr, HGSMI_CH_VBVA, VBVA_VDMA_CMD);
809 Assert(pDr);
810 if (pDr)
811 memset (pDr, 0, cbDr);
812 else
813 drprintf((__FUNCTION__": VBoxSHGSMICommandAlloc returned NULL\n"));
814
815 return pDr;
816}
817
818static DECLCALLBACK(void) vboxVdmaCBufDrCompletion(struct _HGSMIHEAP * pHeap, void *pvCmd, void *pvContext)
819{
820 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pvContext;
821 PVBOXVDMAINFO pInfo = &pDevExt->u.primary.Vdma;
822
823 vboxVdmaCBufDrFree (pInfo, (PVBOXVDMACBUF_DR)pvCmd);
824}
825
826static DECLCALLBACK(void) vboxVdmaCBufDrCompletionIrq(struct _HGSMIHEAP * pHeap, void *pvCmd, void *pvContext,
827 PFNVBOXSHGSMICMDCOMPLETION *ppfnCompletion, void **ppvCompletion)
828{
829 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pvContext;
830 PVBOXVDMAINFO pVdma = &pDevExt->u.primary.Vdma;
831 DXGKARGCB_NOTIFY_INTERRUPT_DATA notify;
832 PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)pvCmd;
833
834 memset(&notify, 0, sizeof(DXGKARGCB_NOTIFY_INTERRUPT_DATA));
835
836 PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)pDr->u64GuestContext;
837
838 if (RT_SUCCESS(pDr->rc))
839 {
840 notify.InterruptType = DXGK_INTERRUPT_DMA_COMPLETED;
841 notify.DmaCompleted.SubmissionFenceId = pDr->u32FenceId;
842 if (pContext)
843 {
844 notify.DmaCompleted.NodeOrdinal = pContext->NodeOrdinal;
845 notify.DmaCompleted.EngineOrdinal = 0;
846 pContext->uLastCompletedCmdFenceId = pDr->u32FenceId;
847 }
848 else
849 pVdma->uLastCompletedPagingBufferCmdFenceId = pDr->u32FenceId;
850 pDevExt->bSetNotifyDxDpc = TRUE;
851 }
852 else if (pDr->rc == VERR_INTERRUPTED)
853 {
854 notify.InterruptType = DXGK_INTERRUPT_DMA_PREEMPTED;
855 notify.DmaPreempted.PreemptionFenceId = pDr->u32FenceId;
856 if (pContext)
857 {
858 notify.DmaPreempted.LastCompletedFenceId = pContext->uLastCompletedCmdFenceId;
859 notify.DmaPreempted.NodeOrdinal = pContext->NodeOrdinal;
860 notify.DmaPreempted.EngineOrdinal = 0;
861 }
862 else
863 notify.DmaPreempted.LastCompletedFenceId = pVdma->uLastCompletedPagingBufferCmdFenceId;
864
865 pDevExt->bSetNotifyDxDpc = TRUE;
866 }
867 else
868 {
869 AssertBreakpoint();
870 notify.InterruptType = DXGK_INTERRUPT_DMA_FAULTED;
871 notify.DmaFaulted.FaultedFenceId = pDr->u32FenceId;
872 notify.DmaFaulted.Status = STATUS_UNSUCCESSFUL; /* @todo: better status ? */
873 if (pContext)
874 {
875 notify.DmaFaulted.NodeOrdinal = pContext->NodeOrdinal;
876 notify.DmaFaulted.EngineOrdinal = 0;
877 }
878 pDevExt->bSetNotifyDxDpc = TRUE;
879 }
880
881 pDevExt->u.primary.DxgkInterface.DxgkCbNotifyInterrupt(pDevExt->u.primary.DxgkInterface.DeviceHandle, &notify);
882
883 /* inform SHGSMI we want to be called at DPC later */
884 *ppfnCompletion = vboxVdmaCBufDrCompletion;
885 *ppvCompletion = pvContext;
886}
887
888int vboxVdmaCBufDrSubmit(PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
889{
890 const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepAsynchIrq (&pInfo->CmdHeap, pDr, vboxVdmaCBufDrCompletionIrq, pDevExt, VBOXSHGSMI_FLAG_GH_ASYNCH_FORCE);
891 Assert(pHdr);
892 int rc = VERR_GENERAL_FAILURE;
893 if (pHdr)
894 {
895 do
896 {
897 HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pInfo->CmdHeap, pHdr);
898 Assert(offCmd != HGSMIOFFSET_VOID);
899 if (offCmd != HGSMIOFFSET_VOID)
900 {
901 rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
902 AssertRC(rc);
903 if (RT_SUCCESS(rc))
904 {
905 VBoxSHGSMICommandDoneAsynch(&pInfo->CmdHeap, pHdr);
906 AssertRC(rc);
907 break;
908 }
909 }
910 else
911 rc = VERR_INVALID_PARAMETER;
912 /* fail to submit, cancel it */
913 VBoxSHGSMICommandCancelAsynch(&pInfo->CmdHeap, pHdr);
914 } while (0);
915 }
916 else
917 rc = VERR_INVALID_PARAMETER;
918 return rc;
919}
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