text:
/* $Id: VBoxMPVdma.cpp $ */

/** @file
 * VBox WDDM Miniport driver
 */

/*
 * Copyright (C) 2011-2012 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

#include "VBoxMPWddm.h"
#include "common/VBoxMPCommon.h"
#include "VBoxMPVdma.h"
#include "VBoxMPVhwa.h"
#include <iprt/asm.h>
#include "VBoxMPCr.h"
#include <iprt/mem.h>

#ifdef VBOX_WITH_CROGL
# include <packer.h>
#endif

static NTSTATUS vboxVdmaCrCtlGetDefaultClientId(PVBOXMP_DEVEXT pDevExt, uint32_t *pu32ClienID);

NTSTATUS vboxVdmaPipeConstruct(PVBOXVDMAPIPE pPipe)
{
    KeInitializeSpinLock(&pPipe->SinchLock);
    KeInitializeEvent(&pPipe->Event, SynchronizationEvent, FALSE);
    InitializeListHead(&pPipe->CmdListHead);
    pPipe->enmState = VBOXVDMAPIPE_STATE_CREATED;
    pPipe->bNeedNotify = true;
    return STATUS_SUCCESS;
}

NTSTATUS vboxVdmaPipeSvrOpen(PVBOXVDMAPIPE pPipe)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KIRQL OldIrql;
    KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
    switch (pPipe->enmState)
    {
        case VBOXVDMAPIPE_STATE_CREATED:
            pPipe->enmState = VBOXVDMAPIPE_STATE_OPENNED;
            pPipe->bNeedNotify = false;
            break;
        case VBOXVDMAPIPE_STATE_OPENNED:
            pPipe->bNeedNotify = false;
            break;
        default:
            AssertBreakpoint();
            Status = STATUS_INVALID_PIPE_STATE;
            break;
    }

    KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
    return Status;
}

NTSTATUS vboxVdmaPipeSvrClose(PVBOXVDMAPIPE pPipe)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KIRQL OldIrql;
    KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
            || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
    switch (pPipe->enmState)
    {
        case VBOXVDMAPIPE_STATE_CLOSING:
            pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
            break;
        case VBOXVDMAPIPE_STATE_CLOSED:
            break;
        default:
            AssertBreakpoint();
            Status = STATUS_INVALID_PIPE_STATE;
            break;
    }

    KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
    return Status;
}

NTSTATUS vboxVdmaPipeCltClose(PVBOXVDMAPIPE pPipe)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KIRQL OldIrql;
    KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
    bool bNeedNotify = false;
    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
                || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED
                ||  pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
    switch (pPipe->enmState)
    {
        case VBOXVDMAPIPE_STATE_OPENNED:
            pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSING;
            bNeedNotify = pPipe->bNeedNotify;
            pPipe->bNeedNotify = false;
            break;
        case VBOXVDMAPIPE_STATE_CREATED:
            pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
            pPipe->bNeedNotify = false;
            break;
        case VBOXVDMAPIPE_STATE_CLOSED:
            break;
        default:
            AssertBreakpoint();
            Status = STATUS_INVALID_PIPE_STATE;
            break;
    }

    KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);

    if (bNeedNotify)
    {
        KeSetEvent(&pPipe->Event, 0, FALSE);
    }
    return Status;
}

NTSTATUS vboxVdmaPipeDestruct(PVBOXVDMAPIPE pPipe)
{
    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
            || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
    /* ensure the pipe is closed */
    NTSTATUS Status = vboxVdmaPipeCltClose(pPipe);
    Assert(Status == STATUS_SUCCESS);

    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);

    return Status;
}

NTSTATUS vboxVdmaPipeSvrCmdGetList(PVBOXVDMAPIPE pPipe, PLIST_ENTRY pDetachHead)
{
    PLIST_ENTRY pEntry = NULL;
    KIRQL OldIrql;
    NTSTATUS Status = STATUS_SUCCESS;
    VBOXVDMAPIPE_STATE enmState = VBOXVDMAPIPE_STATE_CLOSED;
    do
    {
        bool bListEmpty = true;
        KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
        Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
                || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
        Assert(pPipe->enmState >= VBOXVDMAPIPE_STATE_OPENNED);
        enmState = pPipe->enmState;
        if (enmState >= VBOXVDMAPIPE_STATE_OPENNED)
        {
            vboxVideoLeDetach(&pPipe->CmdListHead, pDetachHead);
            bListEmpty = !!(IsListEmpty(pDetachHead));
            pPipe->bNeedNotify = bListEmpty;
        }
        else
        {
            KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
            Status = STATUS_INVALID_PIPE_STATE;
            break;
        }

        KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);

        if (!bListEmpty)
        {
            Assert(Status == STATUS_SUCCESS);
            break;
        }

        if (enmState == VBOXVDMAPIPE_STATE_OPENNED)
        {
            Status = KeWaitForSingleObject(&pPipe->Event, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
            Assert(Status == STATUS_SUCCESS);
            if (Status != STATUS_SUCCESS)
                break;
        }
        else
        {
            Assert(enmState == VBOXVDMAPIPE_STATE_CLOSING);
            Status = STATUS_PIPE_CLOSING;
            break;
        }
    } while (1);

    return Status;
}

NTSTATUS vboxVdmaPipeCltCmdPut(PVBOXVDMAPIPE pPipe, PVBOXVDMAPIPE_CMD_HDR pCmd)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KIRQL OldIrql;
    bool bNeedNotify = false;

    KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);

    Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED);
    if (pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED)
    {
        bNeedNotify = pPipe->bNeedNotify;
        InsertHeadList(&pPipe->CmdListHead, &pCmd->ListEntry);
        pPipe->bNeedNotify = false;
    }
    else
        Status = STATUS_INVALID_PIPE_STATE;

    KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);

    if (bNeedNotify)
    {
        KeSetEvent(&pPipe->Event, 0, FALSE);
    }

    return Status;
}

DECLINLINE(void) vboxVdmaDirtyRectsCalcIntersection(const RECT *pArea, const VBOXWDDM_RECTS_INFO *pRects, PVBOXWDDM_RECTS_INFO pResult)
{
    uint32_t cRects = 0;
    for (uint32_t i = 0; i < pRects->cRects; ++i)
    {
        if (vboxWddmRectIntersection(pArea, &pRects->aRects[i], &pResult->aRects[cRects]))
        {
            ++cRects;
        }
    }

    pResult->cRects = cRects;
}

DECLINLINE(bool) vboxVdmaDirtyRectsHasIntersections(const RECT *paRects1, uint32_t cRects1, const RECT *paRects2, uint32_t cRects2)
{
    RECT tmpRect;
    for (uint32_t i = 0; i < cRects1; ++i)
    {
        const RECT * pRect1 = &paRects1[i];
        for (uint32_t j = 0; j < cRects2; ++j)
        {
            const RECT * pRect2 = &paRects2[j];
            if (vboxWddmRectIntersection(pRect1, pRect2, &tmpRect))
                return true;
        }
    }
    return false;
}

DECLINLINE(bool) vboxVdmaDirtyRectsIsCover(const RECT *paRects, uint32_t cRects, const RECT *paRectsCovered, uint32_t cRectsCovered)
{
    for (uint32_t i = 0; i < cRectsCovered; ++i)
    {
        const RECT * pRectCovered = &paRectsCovered[i];
        uint32_t j = 0;
        for (; j < cRects; ++j)
        {
            const RECT * pRect = &paRects[j];
            if (vboxWddmRectIsCoveres(pRect, pRectCovered))
                break;
        }
        if (j == cRects)
            return false;
    }
    return true;
}
#ifdef VBOX_WITH_CROGL
NTSTATUS vboxVdmaPostHideSwapchain(PVBOXWDDM_SWAPCHAIN pSwapchain)
{
    Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
    uint32_t cbCmdInternal = VBOXVIDEOCM_CMD_RECTS_INTERNAL_SIZE4CRECTS(0);
    PVBOXVIDEOCM_CMD_RECTS_INTERNAL pCmdInternal =
            (PVBOXVIDEOCM_CMD_RECTS_INTERNAL)vboxVideoCmCmdCreate(&pSwapchain->pContext->CmContext, cbCmdInternal);
    Assert(pCmdInternal);
    if (pCmdInternal)
    {
        pCmdInternal->hSwapchainUm = pSwapchain->hSwapchainUm;
        pCmdInternal->Cmd.fFlags.Value = 0;
        pCmdInternal->Cmd.fFlags.bAddHiddenRects = 1;
        pCmdInternal->Cmd.fFlags.bHide = 1;
        pCmdInternal->Cmd.RectsInfo.cRects = 0;
        vboxVideoCmCmdSubmit(pCmdInternal, VBOXVIDEOCM_SUBMITSIZE_DEFAULT);
        return STATUS_SUCCESS;
    }
    return STATUS_NO_MEMORY;
}
#endif
static VOID vboxWddmBltPipeRectsTranslate(VBOXVDMAPIPE_RECTS *pRects, int x, int y)
{
    vboxWddmRectTranslate(&pRects->ContextRect, x, y);

    for (UINT i = 0; i < pRects->UpdateRects.cRects; ++i)
    {
        vboxWddmRectTranslate(&pRects->UpdateRects.aRects[i], x, y);
    }
}

static VBOXVDMAPIPE_RECTS * vboxWddmBltPipeRectsDup(const VBOXVDMAPIPE_RECTS *pRects)
{
    const size_t cbDup = RT_OFFSETOF(VBOXVDMAPIPE_RECTS, UpdateRects.aRects[pRects->UpdateRects.cRects]);
    VBOXVDMAPIPE_RECTS *pDup = (VBOXVDMAPIPE_RECTS*)vboxWddmMemAllocZero(cbDup);
    if (!pDup)
    {
        WARN(("vboxWddmMemAllocZero failed"));
        return NULL;
    }
    memcpy(pDup, pRects, cbDup);
    return pDup;
}
#ifdef VBOX_WITH_CROGL
typedef struct VBOXMP_VDMACR_WRITECOMPLETION
{
    void *pvBufferToFree;
} VBOXMP_VDMACR_WRITECOMPLETION, *PVBOXMP_VDMACR_WRITECOMPLETION;

static DECLCALLBACK(void) vboxVdmaCrWriteCompletion(PVBOXMP_CRSHGSMITRANSPORT pCon, int rc, void *pvCtx)
{
    PVBOXMP_VDMACR_WRITECOMPLETION pData = (PVBOXMP_VDMACR_WRITECOMPLETION)pvCtx;
    void* pvBufferToFree = pData->pvBufferToFree;
    if (pvBufferToFree)
        VBoxMpCrShgsmiTransportBufFree(pCon, pvBufferToFree);

    VBoxMpCrShgsmiTransportCmdTermWriteAsync(pCon, pvCtx);
}

typedef struct VBOXMP_VDMACR_WRITEREADCOMPLETION
{
    void *pvBufferToFree;
    void *pvContext;
} VBOXMP_VDMACR_WRITEREADCOMPLETION, *PVBOXMP_VDMACR_WRITEREADCOMPLETION;

void vboxVdmaCrSubmitWriteReadAsyncGenericCompletion(PVBOXMP_CRSHGSMITRANSPORT pCon, void *pvCtx)
{
    PVBOXMP_VDMACR_WRITEREADCOMPLETION pData = (PVBOXMP_VDMACR_WRITEREADCOMPLETION)pvCtx;
    void* pvBufferToFree = pData->pvBufferToFree;
    if (pvBufferToFree)
        VBoxMpCrShgsmiTransportBufFree(pCon, pvBufferToFree);

    VBoxMpCrShgsmiTransportCmdTermWriteReadAsync(pCon, pvCtx);
}

NTSTATUS vboxVdmaCrSubmitWriteReadAsync(PVBOXMP_DEVEXT pDevExt, VBOXMP_CRPACKER *pCrPacker, uint32_t u32CrConClientID, PFNVBOXMP_CRSHGSMITRANSPORT_SENDWRITEREADASYNC_COMPLETION pfnCompletion, void *pvCompletion)
{
    Assert(u32CrConClientID);
    NTSTATUS Status = STATUS_SUCCESS;
    uint32_t cbBuffer;
    void * pvPackBuffer;
    void * pvBuffer = VBoxMpCrPackerTxBufferComplete(pCrPacker, &cbBuffer, &pvPackBuffer);
    if (pvBuffer)
    {
        PVBOXMP_VDMACR_WRITEREADCOMPLETION pvCompletionData = (PVBOXMP_VDMACR_WRITEREADCOMPLETION)VBoxMpCrShgsmiTransportCmdCreateWriteReadAsync(&pDevExt->CrHgsmiTransport, u32CrConClientID, pvBuffer, cbBuffer,
                pfnCompletion, sizeof (*pvCompletionData));
        if (pvCompletionData)
        {
            pvCompletionData->pvBufferToFree = pvPackBuffer;
            pvCompletionData->pvContext = pvCompletion;
            int rc = VBoxMpCrShgsmiTransportCmdSubmitWriteReadAsync(&pDevExt->CrHgsmiTransport, pvCompletionData);
            if (RT_SUCCESS(rc))
            {
                return STATUS_SUCCESS;
            }
            WARN(("VBoxMpCrShgsmiTransportCmdSubmitWriteReadAsync failed, rc %d", rc));
            Status = STATUS_UNSUCCESSFUL;
            VBoxMpCrShgsmiTransportCmdTermWriteReadAsync(&pDevExt->CrHgsmiTransport, pvCompletionData);
        }
        else
        {
            WARN(("VBoxMpCrShgsmiTransportCmdCreateWriteReadAsync failed"));
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return Status;
}

NTSTATUS vboxVdmaCrSubmitWriteAsync(PVBOXMP_DEVEXT pDevExt, VBOXMP_CRPACKER *pCrPacker, uint32_t u32CrConClientID)
{
    Assert(u32CrConClientID);
    NTSTATUS Status = STATUS_SUCCESS;
    uint32_t cbBuffer;
    void * pvPackBuffer;
    void * pvBuffer = VBoxMpCrPackerTxBufferComplete(pCrPacker, &cbBuffer, &pvPackBuffer);
    if (pvBuffer)
    {
        PVBOXMP_VDMACR_WRITECOMPLETION pvCompletionData = (PVBOXMP_VDMACR_WRITECOMPLETION)VBoxMpCrShgsmiTransportCmdCreateWriteAsync(&pDevExt->CrHgsmiTransport, u32CrConClientID, pvBuffer, cbBuffer,
                vboxVdmaCrWriteCompletion, sizeof (*pvCompletionData));
        if (pvCompletionData)
        {
            pvCompletionData->pvBufferToFree = pvPackBuffer;
            int rc = VBoxMpCrShgsmiTransportCmdSubmitWriteAsync(&pDevExt->CrHgsmiTransport, pvCompletionData);
            if (RT_SUCCESS(rc))
            {
                return STATUS_SUCCESS;
            }
            WARN(("VBoxMpCrShgsmiTransportCmdSubmitWriteAsync failed, rc %d", rc));
            Status = STATUS_UNSUCCESSFUL;
            VBoxMpCrShgsmiTransportCmdTermWriteAsync(&pDevExt->CrHgsmiTransport, pvCompletionData);
        }
        else
        {
            WARN(("VBoxMpCrShgsmiTransportCmdCreateWriteAsync failed"));
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return Status;
}

static NTSTATUS vboxVdmaVRegGet(PVBOXWDDM_SWAPCHAIN pSwapchain, const RTRECT *pCtxRect, uint32_t *pcVRects, RTRECT **ppVRectsBuff, uint32_t *pcVRectsBuff)
{
    RTRECT *pVRectsBuff = *ppVRectsBuff;
    uint32_t cVRectsBuff = *pcVRectsBuff;
    uint32_t cVRects = VBoxVrListRectsCount(&pSwapchain->VisibleRegions);
    if (cVRectsBuff < cVRects)
    {
        if (pVRectsBuff)
            vboxWddmMemFree(pVRectsBuff);
        pVRectsBuff = (RTRECT*)vboxWddmMemAlloc(cVRects * sizeof (pVRectsBuff[0]));
        if (!pVRectsBuff)
        {
            WARN(("vboxWddmMemAlloc failed"));
            *pcVRectsBuff = 0;
            *ppVRectsBuff = NULL;
            *pcVRects = NULL;
            return STATUS_NO_MEMORY;
        }

        cVRectsBuff = cVRects;
        *pcVRectsBuff = cVRectsBuff;
        *ppVRectsBuff = pVRectsBuff;
    }

    int rc = VBoxVrListRectsGet(&pSwapchain->VisibleRegions, cVRects, pVRectsBuff);
    AssertRC(rc);
    if (pCtxRect->xLeft || pCtxRect->yTop)
    {
        for (UINT i = 0; i < cVRects; ++i)
        {
            VBoxRectTranslate(&pVRectsBuff[i], -pCtxRect->xLeft, -pCtxRect->yTop);
        }
    }

    *pcVRects = cVRects;
    return STATUS_SUCCESS;
}

/**
 * @param pDevExt
 */
static NTSTATUS vboxVdmaProcessVRegCmdLegacy(PVBOXMP_DEVEXT pDevExt,
        VBOXMP_CRPACKER *pCrPacker,
        uint32_t u32CrConClientID,
        PVBOXWDDM_SOURCE pSource,
        PVBOXWDDM_SWAPCHAIN pSwapchain,
        const RECT *pSrcRect,
        const VBOXVDMAPIPE_RECTS *pContextRects)
{
    VBOXVDMAPIPE_RECTS *pRectsToFree = NULL;
    POINT pos = pSource->VScreenPos;
    if (pos.x || pos.y)
    {
        pRectsToFree = vboxWddmBltPipeRectsDup(pContextRects);
        /* note: do NOT translate the src rect since it is used for screen pos calculation */
        vboxWddmBltPipeRectsTranslate(pRectsToFree, pos.x, pos.y);
        pContextRects = pRectsToFree;
    }
    const VBOXWDDM_RECTS_INFO *pRects = &pContextRects->UpdateRects;
    NTSTATUS Status = STATUS_SUCCESS;
    int rc;
    bool fCurChanged = FALSE, fCurRectChanged = FALSE;
    POINT CurPos;
    RTRECT *pVRectsBuff = NULL;
    uint32_t cVRectsBuff = 0;
    VBOXWDDM_CTXLOCK_DATA

    VBOXWDDM_CTXLOCK_LOCK(pDevExt);

    if (pSwapchain)
    {
        CurPos.x = pContextRects->ContextRect.left - pSrcRect->left;
        CurPos.y = pContextRects->ContextRect.top - pSrcRect->top;

        if (CurPos.x != pSwapchain->Pos.x || CurPos.y != pSwapchain->Pos.y)
        {
#if 0
            if (pSwapchain->Pos.x != VBOXWDDM_INVALID_COORD)
                VBoxVrListTranslate(&pSwapchain->VisibleRegions, pSwapchain->Pos.x - CurPos.x, pSwapchain->Pos.y - CurPos.y);
            else
#endif
                VBoxVrListClear(&pSwapchain->VisibleRegions);
            fCurRectChanged = TRUE;
            pSwapchain->Pos = CurPos;
        }

        rc = VBoxVrListRectsAdd(&pSwapchain->VisibleRegions, pRects->cRects, (const RTRECT*)pRects->aRects, &fCurChanged);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxWddmVrListRectsAdd failed, rc %d!", rc));
            Status = STATUS_UNSUCCESSFUL;
            goto done;
        }

        /* visible rects of different windows do not intersect,
         * so if the given window visible rects did not increase, others have not changed either */
        if (!fCurChanged && !fCurRectChanged)
            goto done;
    }

    /* before posting the add visible rects diff, we need to first hide rects for other windows */

    for (PLIST_ENTRY pCur = pDevExt->SwapchainList3D.Flink; pCur != &pDevExt->SwapchainList3D; pCur = pCur->Flink)
    {
        if (pCur != &pSwapchain->DevExtListEntry)
        {
            PVBOXWDDM_SWAPCHAIN pCurSwapchain = VBOXWDDMENTRY_2_SWAPCHAIN(pCur);
            PVBOXWDDM_CONTEXT pCurContext = pCurSwapchain->pContext;
            PVBOXMP_CRPACKER pCurPacker = &pCurContext->CrPacker;
            bool fChanged = FALSE;

            rc = VBoxVrListRectsSubst(&pCurSwapchain->VisibleRegions, pRects->cRects, (const RTRECT*)pRects->aRects, &fChanged);
            if (!RT_SUCCESS(rc))
            {
                WARN(("VBoxWddmVrListRectsAdd failed, rc %d!", rc));
                Status = STATUS_UNSUCCESSFUL;
                goto done;
            }

            if (!fChanged)
                continue;

            uint32_t cVRects;
            RTRECT CurCtxRect;
            CurCtxRect.xLeft = pCurSwapchain->Pos.x;
            CurCtxRect.yTop = pCurSwapchain->Pos.y;
            CurCtxRect.xRight = CurCtxRect.xLeft + pCurSwapchain->width;
            CurCtxRect.yBottom = CurCtxRect.yTop + pCurSwapchain->height;
            Status = vboxVdmaVRegGet(pCurSwapchain, &CurCtxRect, &cVRects, &pVRectsBuff, &cVRectsBuff);
            if (!NT_SUCCESS(Status))
            {
                WARN(("vboxVdmaVRegGet Status 0x%x", Status));
                goto done;
            }

            void *pvCommandBuffer = NULL;
            uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE;
            cbCommandBuffer += VBOXMP_CRCMD_SIZE_WINDOWVISIBLEREGIONS(cVRects);

            pvCommandBuffer = VBoxMpCrShgsmiTransportBufAlloc(&pDevExt->CrHgsmiTransport, cbCommandBuffer);
            if (!pvCommandBuffer)
            {
                WARN(("VBoxMpCrShgsmiTransportBufAlloc failed!"));
                Status = VERR_OUT_OF_RESOURCES;
                goto done;
            }

            VBoxMpCrPackerTxBufferInit(pCurPacker, pvCommandBuffer, cbCommandBuffer, 1);

            Assert(pCurSwapchain->winHostID);
            crPackWindowVisibleRegion(&pCurPacker->CrPacker, pCurSwapchain->winHostID, cVRects, (GLint*)pVRectsBuff);

            Status = vboxVdmaCrSubmitWriteAsync(pDevExt, pCurPacker, pCurContext->u32CrConClientID);
            if (!NT_SUCCESS(Status))
            {
                WARN(("vboxVdmaCrSubmitWriteAsync failed Status 0x%x", Status));
                VBoxMpCrShgsmiTransportBufFree(&pDevExt->CrHgsmiTransport, pvCommandBuffer);
            }
        }
    }

    if (!pSwapchain)
        goto done;

    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE, cCommands = 0;

    uint32_t cVRects;
    Status = vboxVdmaVRegGet(pSwapchain, (const RTRECT *)&pContextRects->ContextRect, &cVRects, &pVRectsBuff, &cVRectsBuff);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaVRegGet Status 0x%x", Status));
        goto done;
    }

    ++cCommands;
    cbCommandBuffer += VBOXMP_CRCMD_SIZE_WINDOWVISIBLEREGIONS(cVRects);

    if (fCurRectChanged && fCurChanged)
    {
        ++cCommands;
        cbCommandBuffer += VBOXMP_CRCMD_SIZE_WINDOWPOSITION;
    }

    if (!pSwapchain->fExposed)
    {
        ++cCommands;
        cbCommandBuffer += VBOXMP_CRCMD_SIZE_WINDOWSHOW;
        ++cCommands;
        cbCommandBuffer += VBOXMP_CRCMD_SIZE_WINDOWSIZE;
    }

    void *pvCommandBuffer = VBoxMpCrShgsmiTransportBufAlloc(&pDevExt->CrHgsmiTransport, cbCommandBuffer);
    if (!pvCommandBuffer)
    {
        WARN(("VBoxMpCrShgsmiTransportBufAlloc failed!"));
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    VBoxMpCrPackerTxBufferInit(pCrPacker, pvCommandBuffer, cbCommandBuffer, cCommands);

    Assert(pSwapchain->winHostID);

    if (fCurRectChanged && fCurChanged)
        crPackWindowPosition(&pCrPacker->CrPacker, pSwapchain->winHostID, CurPos.x, CurPos.y);

    if (!pSwapchain->fExposed)
    {
        crPackWindowSize(&pCrPacker->CrPacker, pSwapchain->winHostID, pSwapchain->width, pSwapchain->height);
        crPackWindowShow(&pCrPacker->CrPacker, pSwapchain->winHostID, TRUE);
        pSwapchain->fExposed = TRUE;
    }

    crPackWindowVisibleRegion(&pCrPacker->CrPacker, pSwapchain->winHostID, cVRects, (GLint*)pVRectsBuff);

    Status = vboxVdmaCrSubmitWriteAsync(pDevExt, pCrPacker, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrSubmitWriteAsync failed Status 0x%x", Status));
        VBoxMpCrShgsmiTransportBufFree(&pDevExt->CrHgsmiTransport, pvCommandBuffer);
    }

done:
    VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);

    if (pRectsToFree)
        vboxWddmMemFree(pRectsToFree);

    if (pVRectsBuff)
        vboxWddmMemFree(pVRectsBuff);

    return Status;
}
#endif

static NTSTATUS vboxVdmaGgDmaColorFill(PVBOXMP_DEVEXT pDevExt, VBOXVDMA_CLRFILL *pCF)
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    Assert (pDevExt->pvVisibleVram);
    if (pDevExt->pvVisibleVram)
    {
        PVBOXWDDM_ALLOCATION pAlloc = pCF->Alloc.pAlloc;
        if (pAlloc->AllocData.Addr.SegmentId && pAlloc->AllocData.Addr.SegmentId != 1)
        {
            WARN(("request to collor fill invalid allocation"));
            return STATUS_INVALID_PARAMETER;
        }

        VBOXVIDEOOFFSET offVram = vboxWddmAddrFramOffset(&pAlloc->AllocData.Addr);
        if (offVram != VBOXVIDEOOFFSET_VOID)
        {
            RECT UnionRect = {0};
            uint8_t *pvMem = pDevExt->pvVisibleVram + offVram;
            UINT bpp = pAlloc->AllocData.SurfDesc.bpp;
            Assert(bpp);
            Assert(((bpp * pAlloc->AllocData.SurfDesc.width) >> 3) == pAlloc->AllocData.SurfDesc.pitch);
            switch (bpp)
            {
                case 32:
                {
                    uint8_t bytestPP = bpp >> 3;
                    for (UINT i = 0; i < pCF->Rects.cRects; ++i)
                    {
                        RECT *pRect = &pCF->Rects.aRects[i];
                        for (LONG ir = pRect->top; ir < pRect->bottom; ++ir)
                        {
                            uint32_t * pvU32Mem = (uint32_t*)(pvMem + (ir * pAlloc->AllocData.SurfDesc.pitch) + (pRect->left * bytestPP));
                            uint32_t cRaw = pRect->right - pRect->left;
                            Assert(pRect->left >= 0);
                            Assert(pRect->right <= (LONG)pAlloc->AllocData.SurfDesc.width);
                            Assert(pRect->top >= 0);
                            Assert(pRect->bottom <= (LONG)pAlloc->AllocData.SurfDesc.height);
                            for (UINT j = 0; j < cRaw; ++j)
                            {
                                *pvU32Mem = pCF->Color;
                                ++pvU32Mem;
                            }
                        }
                        vboxWddmRectUnited(&UnionRect, &UnionRect, pRect);
                    }
                    Status = STATUS_SUCCESS;
                    break;
                }
                case 16:
                case 8:
                default:
                    AssertBreakpoint();
                    break;
            }

            if (Status == STATUS_SUCCESS)
            {
                if (pAlloc->AllocData.SurfDesc.VidPnSourceId != D3DDDI_ID_UNINITIALIZED
                        && VBOXWDDM_IS_FB_ALLOCATION(pDevExt, pAlloc)
                        && pAlloc->bVisible
                        )
                {
                    if (!vboxWddmRectIsEmpty(&UnionRect))
                    {
                        PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[pCF->Alloc.pAlloc->AllocData.SurfDesc.VidPnSourceId];
                        uint32_t cUnlockedVBVADisabled = ASMAtomicReadU32(&pDevExt->cUnlockedVBVADisabled);
                        if (!cUnlockedVBVADisabled)
                        {
                            VBOXVBVA_OP(ReportDirtyRect, pDevExt, pSource, &UnionRect);
                        }
                        else
                        {
                            VBOXVBVA_OP_WITHLOCK(ReportDirtyRect, pDevExt, pSource, &UnionRect);
                        }
                    }
                }
                else
                {
                    AssertBreakpoint();
                }
            }
        }
        else
            WARN(("invalid offVram"));
    }

    return Status;
}

NTSTATUS vboxVdmaGgDmaBltPerform(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_ALLOC_DATA pSrcAlloc, RECT* pSrcRect,
        PVBOXWDDM_ALLOC_DATA pDstAlloc, RECT* pDstRect)
{
    uint8_t* pvVramBase = pDevExt->pvVisibleVram;
    /* we do not support stretching */
    uint32_t srcWidth = pSrcRect->right - pSrcRect->left;
    uint32_t srcHeight = pSrcRect->bottom - pSrcRect->top;
    uint32_t dstWidth = pDstRect->right - pDstRect->left;
    uint32_t dstHeight = pDstRect->bottom - pDstRect->top;
    Assert(srcHeight == dstHeight);
    Assert(dstWidth == srcWidth);
    Assert(pDstAlloc->Addr.offVram != VBOXVIDEOOFFSET_VOID);
    Assert(pSrcAlloc->Addr.offVram != VBOXVIDEOOFFSET_VOID);
    D3DDDIFORMAT enmSrcFormat, enmDstFormat;

    enmSrcFormat = pSrcAlloc->SurfDesc.format;
    enmDstFormat = pDstAlloc->SurfDesc.format;

    if (pDstAlloc->Addr.SegmentId && pDstAlloc->Addr.SegmentId != 1)
    {
        WARN(("request to collor blit invalid allocation"));
        return STATUS_INVALID_PARAMETER;
    }

    if (pSrcAlloc->Addr.SegmentId && pSrcAlloc->Addr.SegmentId != 1)
    {
        WARN(("request to collor blit invalid allocation"));
        return STATUS_INVALID_PARAMETER;
    }

    if (enmSrcFormat != enmDstFormat)
    {
        /* just ignore the alpha component
         * this is ok since our software-based stuff can not handle alpha channel in any way */
        enmSrcFormat = vboxWddmFmtNoAlphaFormat(enmSrcFormat);
        enmDstFormat = vboxWddmFmtNoAlphaFormat(enmDstFormat);
        if (enmSrcFormat != enmDstFormat)
        {
            WARN(("color conversion src(%d), dst(%d) not supported!", pSrcAlloc->SurfDesc.format, pDstAlloc->SurfDesc.format));
            return STATUS_INVALID_PARAMETER;
        }
    }
    if (srcHeight != dstHeight)
            return STATUS_INVALID_PARAMETER;
    if (srcWidth != dstWidth)
            return STATUS_INVALID_PARAMETER;
    if (pDstAlloc->Addr.offVram == VBOXVIDEOOFFSET_VOID)
        return STATUS_INVALID_PARAMETER;
    if (pSrcAlloc->Addr.offVram == VBOXVIDEOOFFSET_VOID)
        return STATUS_INVALID_PARAMETER;

    uint8_t *pvDstSurf = pDstAlloc->Addr.SegmentId ? pvVramBase + pDstAlloc->Addr.offVram : (uint8_t*)pDstAlloc->Addr.pvMem;
    uint8_t *pvSrcSurf = pSrcAlloc->Addr.SegmentId ? pvVramBase + pSrcAlloc->Addr.offVram : (uint8_t*)pSrcAlloc->Addr.pvMem;

    if (pDstAlloc->SurfDesc.width == dstWidth
            && pSrcAlloc->SurfDesc.width == srcWidth
            && pSrcAlloc->SurfDesc.width == pDstAlloc->SurfDesc.width)
    {
        Assert(!pDstRect->left);
        Assert(!pSrcRect->left);
        uint32_t cbDstOff = vboxWddmCalcOffXYrd(0 /* x */, pDstRect->top, pDstAlloc->SurfDesc.pitch, pDstAlloc->SurfDesc.format);
        uint32_t cbSrcOff = vboxWddmCalcOffXYrd(0 /* x */, pSrcRect->top, pSrcAlloc->SurfDesc.pitch, pSrcAlloc->SurfDesc.format);
        uint32_t cbSize = vboxWddmCalcSize(pDstAlloc->SurfDesc.pitch, dstHeight, pDstAlloc->SurfDesc.format);
        memcpy(pvDstSurf + cbDstOff, pvSrcSurf + cbSrcOff, cbSize);
    }
    else
    {
        uint32_t cbDstLine =  vboxWddmCalcRowSize(pDstRect->left, pDstRect->right, pDstAlloc->SurfDesc.format);
        uint32_t offDstStart = vboxWddmCalcOffXYrd(pDstRect->left, pDstRect->top, pDstAlloc->SurfDesc.pitch, pDstAlloc->SurfDesc.format);
        Assert(cbDstLine <= pDstAlloc->SurfDesc.pitch);
        uint32_t cbDstSkip = pDstAlloc->SurfDesc.pitch;
        uint8_t * pvDstStart = pvDstSurf + offDstStart;

        uint32_t cbSrcLine = vboxWddmCalcRowSize(pSrcRect->left, pSrcRect->right, pSrcAlloc->SurfDesc.format);
        uint32_t offSrcStart = vboxWddmCalcOffXYrd(pSrcRect->left, pSrcRect->top, pSrcAlloc->SurfDesc.pitch, pSrcAlloc->SurfDesc.format);
        Assert(cbSrcLine <= pSrcAlloc->SurfDesc.pitch);
        uint32_t cbSrcSkip = pSrcAlloc->SurfDesc.pitch;
        const uint8_t * pvSrcStart = pvSrcSurf + offSrcStart;

        uint32_t cRows = vboxWddmCalcNumRows(pDstRect->top, pDstRect->bottom, pDstAlloc->SurfDesc.format);

        Assert(cbDstLine == cbSrcLine);

        for (uint32_t i = 0; i < cRows; ++i)
        {
            memcpy(pvDstStart, pvSrcStart, cbDstLine);
            pvDstStart += cbDstSkip;
            pvSrcStart += cbSrcSkip;
        }
    }
    return STATUS_SUCCESS;
}

/*
 * @return on success the number of bytes the command contained, otherwise - VERR_xxx error code
 */
static NTSTATUS vboxVdmaGgDmaBlt(PVBOXMP_DEVEXT pDevExt, PVBOXVDMA_BLT pBlt)
{
    /* we do not support stretching for now */
    Assert(pBlt->SrcRect.right - pBlt->SrcRect.left == pBlt->DstRects.ContextRect.right - pBlt->DstRects.ContextRect.left);
    Assert(pBlt->SrcRect.bottom - pBlt->SrcRect.top == pBlt->DstRects.ContextRect.bottom - pBlt->DstRects.ContextRect.top);
    if (pBlt->SrcRect.right - pBlt->SrcRect.left != pBlt->DstRects.ContextRect.right - pBlt->DstRects.ContextRect.left)
        return STATUS_INVALID_PARAMETER;
    if (pBlt->SrcRect.bottom - pBlt->SrcRect.top != pBlt->DstRects.ContextRect.bottom - pBlt->DstRects.ContextRect.top)
        return STATUS_INVALID_PARAMETER;
    Assert(pBlt->DstRects.UpdateRects.cRects);

    NTSTATUS Status = STATUS_SUCCESS;

    if (pBlt->DstRects.UpdateRects.cRects)
    {
        for (uint32_t i = 0; i < pBlt->DstRects.UpdateRects.cRects; ++i)
        {
            RECT SrcRect;
            vboxWddmRectTranslated(&SrcRect, &pBlt->DstRects.UpdateRects.aRects[i], -pBlt->DstRects.ContextRect.left, -pBlt->DstRects.ContextRect.top);

            Status = vboxVdmaGgDmaBltPerform(pDevExt, &pBlt->SrcAlloc.pAlloc->AllocData, &SrcRect,
                    &pBlt->DstAlloc.pAlloc->AllocData, &pBlt->DstRects.UpdateRects.aRects[i]);
            Assert(Status == STATUS_SUCCESS);
            if (Status != STATUS_SUCCESS)
                return Status;
        }
    }
    else
    {
        Status = vboxVdmaGgDmaBltPerform(pDevExt, &pBlt->SrcAlloc.pAlloc->AllocData, &pBlt->SrcRect,
                &pBlt->DstAlloc.pAlloc->AllocData, &pBlt->DstRects.ContextRect);
        Assert(Status == STATUS_SUCCESS);
        if (Status != STATUS_SUCCESS)
            return Status;
    }

    return Status;
}

#ifdef VBOX_WITH_CROGL
typedef struct VBOXVDMA_CRRXGENERICSYNC
{
    int rc;
    KEVENT Event;
} VBOXVDMA_CRRXGENERICSYNC, *PVBOXVDMA_CRRXGENERICSYNC;

static DECLCALLBACK(void) vboxVdmaCrRxGenericSyncCompletion(PVBOXMP_CRSHGSMITRANSPORT pCon, int rc, void *pvRx, uint32_t cbRx, void *pvCtx)
{
    PVBOXMP_VDMACR_WRITEREADCOMPLETION pvCompletionData = (PVBOXMP_VDMACR_WRITEREADCOMPLETION)pvCtx;
    PVBOXVDMA_CRRXGENERICSYNC pData = (PVBOXVDMA_CRRXGENERICSYNC)pvCompletionData->pvContext;
    if (RT_SUCCESS(rc))
    {
        rc = VBoxMpCrCmdRxHandler((CRMessageHeader*)pvRx, cbRx);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxMpCrCmdRxHandler failed %d", rc));
        }
    }
    else
    {
        WARN(("rx failure %d", rc));
    }

    pData->rc = rc;

    KeSetEvent(&pData->Event, 0, FALSE);

    vboxVdmaCrSubmitWriteReadAsyncGenericCompletion(pCon, pvCtx);
}

NTSTATUS vboxVdmaCrRxGenericSync(PVBOXMP_DEVEXT pDevExt, VBOXMP_CRPACKER *pCrPacker, uint32_t u32CrConClientID)
{
    VBOXVDMA_CRRXGENERICSYNC Data;
    Data.rc = VERR_NOT_SUPPORTED;
    KeInitializeEvent(&Data.Event, SynchronizationEvent, FALSE);
    NTSTATUS Status = vboxVdmaCrSubmitWriteReadAsync(pDevExt, pCrPacker, u32CrConClientID, vboxVdmaCrRxGenericSyncCompletion, &Data);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrSubmitWriteAsync failed Status 0x%x", Status));
        return Status;
    }

     Status = KeWaitForSingleObject(&Data.Event, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
     if (!NT_SUCCESS(Status))
     {
         WARN(("KeWaitForSingleObject failed Status 0x%x", Status));
         return Status;
     }

     return STATUS_SUCCESS;
}

typedef struct VBOXMP_CRHGSMIMGR
{
    VBOXMP_CRPACKER CrPacker;
    void *pvCommandBuffer;
} VBOXMP_CRHGSMIMGR;

DECLINLINE(CRPackContext*) vboxVdmaCrHmGetPackContext(VBOXMP_CRHGSMIMGR *pMgr)
{
    return &pMgr->CrPacker.CrPacker;
}

NTSTATUS vboxVdmaCrHmCreate(PVBOXMP_DEVEXT pDevExt, VBOXMP_CRHGSMIMGR *pMgr, uint32_t cbCommandBuffer, uint32_t cCommands)
{
    pMgr->pvCommandBuffer = VBoxMpCrShgsmiTransportBufAlloc(&pDevExt->CrHgsmiTransport, cbCommandBuffer);
    if (!pMgr->pvCommandBuffer)
    {
        WARN(("VBoxMpCrShgsmiTransportBufAlloc failed!"));
        return VERR_OUT_OF_RESOURCES;
    }

    VBoxMpCrPackerInit(&pMgr->CrPacker);

    VBoxMpCrPackerTxBufferInit(&pMgr->CrPacker, pMgr->pvCommandBuffer, cbCommandBuffer, cCommands);

    return STATUS_SUCCESS;
}

NTSTATUS vboxVdmaCrHmSubmitWrSync(PVBOXMP_DEVEXT pDevExt, VBOXMP_CRHGSMIMGR *pMgr, uint32_t u32CrConClientID)
{
    NTSTATUS Status = vboxVdmaCrRxGenericSync(pDevExt, &pMgr->CrPacker, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrRxGenericSync failed Status 0x%x", Status));
        VBoxMpCrShgsmiTransportBufFree(&pDevExt->CrHgsmiTransport, pMgr->pvCommandBuffer);
        return Status;
    }

    return STATUS_SUCCESS;
}
#if 0
NTSTATUS vboxVdmaCrCmdGetChromiumParametervCR(PVBOXMP_DEVEXT pDevExt, uint32_t u32CrConClientID, GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid * values)
{
    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE + VBOXMP_CRCMD_SIZE_GETCHROMIUMPARAMETERVCR;
    uint32_t cCommands = 1;

    VBOXMP_CRHGSMIMGR Mgr;
    NTSTATUS Status = vboxVdmaCrHmCreate(pDevExt, &Mgr, cbCommandBuffer, cCommands);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmCreate failed Status 0x%x", Status));
        return Status;
    }

    int dummy = 1;

    crPackGetChromiumParametervCR(vboxVdmaCrHmGetPackContext(&Mgr), target, index, type, count, values, &dummy);

    Status = vboxVdmaCrHmSubmitWrSync(pDevExt, &Mgr, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmSubmitWrSync failed Status 0x%x", Status));
        return Status;
    }

    return STATUS_SUCCESS;
}

static NTSTATUS vboxVdmaCrCmdCreateContext(PVBOXMP_DEVEXT pDevExt, int32_t visualBits, int32_t *pi32CtxID)
{
    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE + VBOXMP_CRCMD_SIZE_CREATECONTEXT;
    uint32_t cCommands = 1;

    VBOXMP_CRHGSMIMGR Mgr;
    NTSTATUS Status = vboxVdmaCrHmCreate(pDevExt, &Mgr, cbCommandBuffer, cCommands);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmCreate failed Status 0x%x", Status));
        return Status;
    }

    int dummy = 1;

    crPackCreateContext(&CrPacker.CrPacker, "", visualBits, 0, &pi32CtxID, &dummy);

    Status = vboxVdmaCrHmSubmitWrSync(pDevExt, &Mgr, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmSubmitWrSync failed Status 0x%x", Status));
        return Status;
    }

    return STATUS_SUCCESS;
}

static NTSTATUS vboxVdmaCrCmdWindowCreate(PVBOXMP_DEVEXT pDevExt, int32_t visualBits, int32_t *pi32WinID)
{
    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE + VBOXMP_CRCMD_SIZE_WINDOWCREATE;
    uint32_t cCommands = 1;

    VBOXMP_CRHGSMIMGR Mgr;
    NTSTATUS Status = vboxVdmaCrHmCreate(pDevExt, &Mgr, cbCommandBuffer, cCommands);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmCreate failed Status 0x%x", Status));
        return Status;
    }

    int dummy = 1;

    crPackWindowCreate(&CrPacker.CrPacker, "", visualBits, 0, &pi32CtxID, &dummy);

    Status = vboxVdmaCrHmSubmitWrSync(pDevExt, &Mgr, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrHmSubmitWrSync failed Status 0x%x", Status));
        return Status;
    }

    return STATUS_SUCCESS;
}

static NTSTATUS vboxVdmaCrCtlGetDefaultCtxId(PVBOXMP_DEVEXT pDevExt, int32_t *pi32CtxID)
{
    if (!pDevExt->i32CrConDefaultCtxID)
    {
        if (!pDevExt->f3DEnabled)
        {
            WARN(("3D disabled, should not be here!"));
            return STATUS_UNSUCCESSFUL;
        }

        uint32_t u32ClienID;
        NTSTATUS Status = vboxVdmaCrCtlGetDefaultClientId(pDevExt, &u32ClienID);
        if (!NT_SUCCESS(Status))
        {
            WARN(("vboxVdmaCrCtlGetDefaultClientId failed, Status %#x", Status));
            return Status;
        }

        Status = vboxVdmaCrCmdWindowCreate(PVBOXMP_DEVEXT pDevExt, int32_t visualBits, int32_t *pi32WinID)

        VBOXMP_CRPACKER CrPacker;
        VBoxMpCrPackerInit(&CrPacker);

        int rc = VBoxMpCrCtlConConnect(pDevExt, &pDevExt->CrCtlCon, CR_PROTOCOL_VERSION_MAJOR, CR_PROTOCOL_VERSION_MINOR, &pDevExt->u32CrConDefaultClientID);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxMpCrCtlConConnect failed, rc %d", rc));
            return STATUS_UNSUCCESSFUL;
        }
    }

    *pi32CtxID = pDevExt->i32CrConDefaultCtxID;
    return STATUS_SUCCESS;
}
#endif

static NTSTATUS vboxVdmaTexPresentSubmit(PVBOXMP_DEVEXT pDevExt,
        VBOXMP_CRPACKER *pCrPacker,
        uint32_t u32CrConClientID,
        uint32_t hostID,
        uint32_t cfg,
        int32_t posX,
        int32_t posY,
        uint32_t cRects,
        const RTRECT*paRects)
{
    Assert(pDevExt->fTexPresentEnabled);

    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE + VBOXMP_CRCMD_SIZE_VBOXTEXPRESENT(cRects);
    uint32_t cCommands = 1;
    void *pvCommandBuffer = VBoxMpCrShgsmiTransportBufAlloc(&pDevExt->CrHgsmiTransport, cbCommandBuffer);
    if (!pvCommandBuffer)
    {
        WARN(("VBoxMpCrShgsmiTransportBufAlloc failed!"));
        return VERR_OUT_OF_RESOURCES;
    }

    VBoxMpCrPackerTxBufferInit(pCrPacker, pvCommandBuffer, cbCommandBuffer, cCommands);

    crPackVBoxTexPresent(&pCrPacker->CrPacker, hostID, cfg, posX, posY, cRects, (int32_t*)paRects);

    NTSTATUS Status = vboxVdmaCrSubmitWriteAsync(pDevExt, pCrPacker, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrSubmitWriteAsync failed Status 0x%x", Status));
        VBoxMpCrShgsmiTransportBufFree(&pDevExt->CrHgsmiTransport, pvCommandBuffer);
    }

    return Status;
}

static NTSTATUS vboxVdmaChromiumParameteriCRSubmit(PVBOXMP_DEVEXT pDevExt,
        VBOXMP_CRPACKER *pCrPacker,
        uint32_t u32CrConClientID,
        uint32_t target, uint32_t value)
{
    Assert(pDevExt->fTexPresentEnabled);

    uint32_t cbCommandBuffer = VBOXMP_CRCMD_HEADER_SIZE + VBOXMP_CRCMD_SIZE_CHROMIUMPARAMETERICR;
    uint32_t cCommands = 1;
    void *pvCommandBuffer = VBoxMpCrShgsmiTransportBufAlloc(&pDevExt->CrHgsmiTransport, cbCommandBuffer);
    if (!pvCommandBuffer)
    {
        WARN(("VBoxMpCrShgsmiTransportBufAlloc failed!"));
        return VERR_OUT_OF_RESOURCES;
    }

    VBoxMpCrPackerTxBufferInit(pCrPacker, pvCommandBuffer, cbCommandBuffer, cCommands);

    crPackChromiumParameteriCR(&pCrPacker->CrPacker, target, value);

    NTSTATUS Status = vboxVdmaCrSubmitWriteAsync(pDevExt, pCrPacker, u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrSubmitWriteAsync failed Status 0x%x", Status));
        VBoxMpCrShgsmiTransportBufFree(&pDevExt->CrHgsmiTransport, pvCommandBuffer);
    }

    return Status;
}

NTSTATUS VBoxVdmaChromiumParameteriCRSubmit(PVBOXMP_DEVEXT pDevExt, uint32_t target, uint32_t value)
{
    uint32_t u32CrConClientID;
    NTSTATUS Status = vboxVdmaCrCtlGetDefaultClientId(pDevExt, &u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrCtlGetDefaultClientId failed Status 0x%x", Status));
        return Status;
    }

    VBOXMP_CRPACKER *pCrPacker = (VBOXMP_CRPACKER *)RTMemTmpAlloc(sizeof(*pCrPacker));
    if (!pCrPacker)
        return STATUS_NO_MEMORY;
    VBoxMpCrPackerInit(pCrPacker);

    Status = vboxVdmaChromiumParameteriCRSubmit(pDevExt, pCrPacker, u32CrConClientID, target, value);
    if (!NT_SUCCESS(Status))
        WARN(("vboxVdmaChromiumParameteriCRSubmit failed Status 0x%x", Status));

    RTMemTmpFree(pCrPacker);
    return Status;
}

static NTSTATUS vboxVdmaCrCtlGetDefaultClientId(PVBOXMP_DEVEXT pDevExt, uint32_t *pu32ClienID)
{
    if (!pDevExt->u32CrConDefaultClientID)
    {
        if (!pDevExt->f3DEnabled)
        {
            WARN(("3D disabled, should not be here!"));
            return STATUS_UNSUCCESSFUL;
        }

        int rc = VBoxMpCrCtlConConnect(pDevExt, &pDevExt->CrCtlCon, CR_PROTOCOL_VERSION_MAJOR, CR_PROTOCOL_VERSION_MINOR, &pDevExt->u32CrConDefaultClientID);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxMpCrCtlConConnect failed, rc %d", rc));
            return STATUS_UNSUCCESSFUL;
        }
    }

    *pu32ClienID = pDevExt->u32CrConDefaultClientID;
    return STATUS_SUCCESS;
}

static NTSTATUS vboxVdmaProcessVRegTexPresent(PVBOXMP_DEVEXT pDevExt,
        VBOXMP_CRPACKER *pCrPacker,
        uint32_t u32CrConClientID,
        const VBOXWDDM_ALLOC_DATA *pSrcAllocData,
        const VBOXWDDM_ALLOC_DATA *pDstAllocData,
        const RECT *pSrcRect, const VBOXVDMAPIPE_RECTS *pDstRects)
{
    D3DDDI_VIDEO_PRESENT_SOURCE_ID srcId = pDstAllocData->SurfDesc.VidPnSourceId;
    if (srcId >= (D3DDDI_VIDEO_PRESENT_SOURCE_ID)VBoxCommonFromDeviceExt(pDevExt)->cDisplays)
    {
        WARN(("invalid srcId"));
        return STATUS_NOT_SUPPORTED;
    }

    VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[srcId];

    bool fPrimary = vboxWddmAddrVramEqual(&pDstAllocData->Addr, &pSource->AllocData.Addr);
    /* we care only about screen regions */
    if (!fPrimary)
    {
        WARN(("non-primary allocation passed to vboxWddmSubmitBltCmd!"));
        return STATUS_NOT_SUPPORTED;
    }

    NTSTATUS Status = STATUS_SUCCESS;
    uint32_t hostID = pSrcAllocData->hostID;
    int rc;
    if (hostID)
    {
//            Assert(pContext->enmType == VBOXWDDM_CONTEXT_TYPE_CUSTOM_3D);
        int32_t posX = pDstRects->ContextRect.left - pSrcRect->left;
        int32_t posY = pDstRects->ContextRect.top - pSrcRect->top;

        Status = vboxVdmaTexPresentSubmit(pDevExt, pCrPacker, u32CrConClientID, hostID, srcId, posX, posY, pDstRects->UpdateRects.cRects, (const RTRECT*)pDstRects->UpdateRects.aRects);
        if (NT_SUCCESS(Status))
        {
            rc = VBoxVrListRectsSubst(&pSource->VrList, pDstRects->UpdateRects.cRects, (const RTRECT*)pDstRects->UpdateRects.aRects, NULL);
            if (RT_SUCCESS(rc))
                pSource->fHas3DVrs = TRUE;
            else
                WARN(("VBoxVrListRectsSubst failed rc %d, ignoring..", rc));
        }
        else
            WARN(("vboxVdmaTexPresentSubmit failed Status 0x%x", Status));
    }
    else
    {
        Assert(fPrimary);

        bool fChanged = false;
        Assert(pSource->pPrimaryAllocation->bVisible);
        rc = VBoxVrListRectsAdd(&pSource->VrList, pDstRects->UpdateRects.cRects, (const RTRECT*)pDstRects->UpdateRects.aRects, &fChanged);
        if (RT_SUCCESS(rc))
        {
            if (fChanged)
            {
                Status = vboxVdmaTexPresentSubmit(pDevExt, pCrPacker, u32CrConClientID, hostID, srcId, 0, 0, pDstRects->UpdateRects.cRects, (const RTRECT*)pDstRects->UpdateRects.aRects);
                if (NT_SUCCESS(Status))
                {
                    if (pSource->fHas3DVrs)
                    {
                        if (VBoxVrListRectsCount(&pSource->VrList) == 1)
                        {
                            RTRECT Rect;
                            VBoxVrListRectsGet(&pSource->VrList, 1, &Rect);
                            if (Rect.xLeft == 0
                                    && Rect.yTop == 0
                                    && Rect.xRight == pDstAllocData->SurfDesc.width
                                    && Rect.yBottom == pDstAllocData->SurfDesc.height)
                            {
                                pSource->fHas3DVrs = FALSE;
                            }
                        }
                    }
                }
                else
                    WARN(("vboxVdmaTexPresentSubmit failed Status 0x%x", Status));
            }
        }
        else
            WARN(("VBoxVrListRectsAdd failed rc %d, ignoring..", rc));
    }

    return Status;
}

static NTSTATUS vboxVdmaProcessVReg(PVBOXMP_DEVEXT pDevExt,
        VBOXMP_CRPACKER *pCrPacker,
        uint32_t u32CrConClientID,
        const VBOXWDDM_ALLOCATION *pSrcAlloc,
        const VBOXWDDM_ALLOCATION *pDstAlloc,
        const RECT *pSrcRect, const VBOXVDMAPIPE_RECTS *pDstRects)
{
    D3DDDI_VIDEO_PRESENT_SOURCE_ID srcId = pDstAlloc->AllocData.SurfDesc.VidPnSourceId;
    VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[srcId];
    NTSTATUS Status = STATUS_SUCCESS;

    if (pDevExt->fTexPresentEnabled)
    {
        Status = vboxVdmaProcessVRegTexPresent(pDevExt, pCrPacker, u32CrConClientID,
                &pSrcAlloc->AllocData, &pDstAlloc->AllocData,
                pSrcRect, pDstRects);
    }
    else
    {
        PVBOXWDDM_SWAPCHAIN pSwapchain = vboxWddmSwapchainRetainByAlloc(pDevExt, pSrcAlloc);

        if (pSwapchain)
        {
            Assert(pSrcAlloc->AllocData.SurfDesc.width == pSwapchain->width);
            Assert(pSrcAlloc->AllocData.SurfDesc.height == pSwapchain->height);
        }

        Status = vboxVdmaProcessVRegCmdLegacy(pDevExt, pCrPacker, u32CrConClientID, pSource, pSwapchain, pSrcRect, pDstRects);
        if (!NT_SUCCESS(Status))
            WARN(("vboxVdmaProcessVRegCmdLegacy failed Status 0x%x", Status));

        if (pSwapchain)
            vboxWddmSwapchainRelease(pSwapchain);
    }

    return Status;
}

NTSTATUS vboxVdmaTexPresentSetAlloc(PVBOXMP_DEVEXT pDevExt, const VBOXWDDM_ALLOC_DATA *pAllocData)
{
    uint32_t u32CrConClientID;
    NTSTATUS Status = vboxVdmaCrCtlGetDefaultClientId(pDevExt, &u32CrConClientID);
    if (!NT_SUCCESS(Status))
    {
        WARN(("vboxVdmaCrCtlGetDefaultClientId failed Status 0x%x", Status));
        return Status;
    }

    VBOXMP_CRPACKER *pCrPacker = (VBOXMP_CRPACKER *)RTMemTmpAlloc(sizeof(*pCrPacker));
    if (!pCrPacker)
        return STATUS_NO_MEMORY;
    VBoxMpCrPackerInit(pCrPacker);

    RECT Rect;
    Rect.left = 0;
    Rect.top = 0;
    Rect.right = pAllocData->SurfDesc.width;
    Rect.bottom = pAllocData->SurfDesc.height;

    if (pDevExt->fCmdVbvaEnabled)
        Status = vboxVdmaTexPresentSubmit(pDevExt, pCrPacker, u32CrConClientID, pAllocData->hostID, pAllocData->SurfDesc.VidPnSourceId, 0, 0, 1, (RTRECT*)&Rect);
    else if (pDevExt->fTexPresentEnabled)
    {
        VBOXVDMAPIPE_RECTS RectInfo;
        RectInfo.ContextRect = Rect;
        RectInfo.UpdateRects.cRects = 1;
        RectInfo.UpdateRects.aRects[0] = Rect;

        Status = vboxVdmaProcessVRegTexPresent(pDevExt, pCrPacker, u32CrConClientID,
                pAllocData, pAllocData,
                &Rect, &RectInfo);
    }
    else
        Status = STATUS_NOT_IMPLEMENTED;

    RTMemTmpFree(pCrPacker);
    return Status;
}

static NTSTATUS vboxVdmaProcessVRegCmd(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_CONTEXT *pContext,
        const VBOXWDDM_DMA_ALLOCINFO *pSrcAllocInfo,
        const VBOXWDDM_DMA_ALLOCINFO *pDstAllocInfo,
        const RECT *pSrcRect, const VBOXVDMAPIPE_RECTS *pDstRects)
{
    return vboxVdmaProcessVReg(pDevExt, &pContext->CrPacker, pContext->u32CrConClientID,
            pSrcAllocInfo->pAlloc, pDstAllocInfo->pAlloc,
            pSrcRect, pDstRects);
}

static void vboxVdmaBltDirtyRectsUpdate(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_SOURCE *pSource, uint32_t cRects, const RECT *paRects)
{
    if (!cRects)
    {
        WARN(("vboxVdmaBltDirtyRectsUpdate: no rects specified"));
        return;
    }

    RECT rect;
    rect = paRects[0];
    for (UINT i = 1; i < cRects; ++i)
    {
        vboxWddmRectUnited(&rect, &rect, &paRects[i]);
    }

    uint32_t cUnlockedVBVADisabled = ASMAtomicReadU32(&pDevExt->cUnlockedVBVADisabled);
    if (!cUnlockedVBVADisabled)
    {
        VBOXVBVA_OP(ReportDirtyRect, pDevExt, pSource, &rect);
    }
    else
    {
        VBOXVBVA_OP_WITHLOCK_ATDPC(ReportDirtyRect, pDevExt, pSource, &rect);
    }
}
#endif

NTSTATUS vboxVdmaProcessBltCmd(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_CONTEXT *pContext, VBOXWDDM_DMA_PRIVATEDATA_BLT *pBlt)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_ALLOCATION pDstAlloc = pBlt->Blt.DstAlloc.pAlloc;
    PVBOXWDDM_ALLOCATION pSrcAlloc = pBlt->Blt.SrcAlloc.pAlloc;
    BOOLEAN fVRAMUpdated = FALSE;
#ifdef VBOX_WITH_CROGL
    if (!pDstAlloc->AllocData.hostID && !pSrcAlloc->AllocData.hostID)
#endif
    {
        /* the allocations contain a real data in VRAM, do blitting */
        vboxVdmaGgDmaBlt(pDevExt, &pBlt->Blt);
        fVRAMUpdated = TRUE;
    }

#ifdef VBOX_WITH_CROGL
    if (VBOXWDDM_IS_FB_ALLOCATION(pDevExt, pDstAlloc)
            && pDstAlloc->bVisible)
    {
        VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[pDstAlloc->AllocData.SurfDesc.VidPnSourceId];
        Assert(pDstAlloc->AllocData.SurfDesc.VidPnSourceId < VBOX_VIDEO_MAX_SCREENS);
        Assert(pSource->pPrimaryAllocation == pDstAlloc);

        if (fVRAMUpdated)
            vboxVdmaBltDirtyRectsUpdate(pDevExt, pSource, pBlt->Blt.DstRects.UpdateRects.cRects, pBlt->Blt.DstRects.UpdateRects.aRects);

        if (pSrcAlloc->AllocData.hostID || (pDevExt->fTexPresentEnabled ? pSource->fHas3DVrs : !!pDevExt->cContexts3D))
        {
            Status = vboxVdmaProcessVRegCmd(pDevExt, pContext, &pBlt->Blt.SrcAlloc, &pBlt->Blt.DstAlloc, &pBlt->Blt.SrcRect, &pBlt->Blt.DstRects);
            if (!NT_SUCCESS(Status))
                WARN(("vboxVdmaProcessVRegCmd failed Status 0x%x", Status));
        }
    }
#endif
    return Status;
}

NTSTATUS vboxVdmaProcessFlipCmd(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_CONTEXT *pContext, VBOXWDDM_DMA_PRIVATEDATA_FLIP *pFlip)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_ALLOCATION pAlloc = pFlip->Flip.Alloc.pAlloc;
    VBOXWDDM_SOURCE *pSource = &pDevExt->aSources[pAlloc->AllocData.SurfDesc.VidPnSourceId];
    vboxWddmAssignPrimary(pSource, pAlloc, pAlloc->AllocData.SurfDesc.VidPnSourceId);
#ifdef VBOX_WITH_CROGL
    if (pAlloc->AllocData.hostID)
    {
        RECT SrcRect;
        VBOXVDMAPIPE_RECTS Rects;
        SrcRect.left = 0;
        SrcRect.top = 0;
        SrcRect.right = pAlloc->AllocData.SurfDesc.width;
        SrcRect.bottom = pAlloc->AllocData.SurfDesc.height;
        Rects.ContextRect.left = 0;
        Rects.ContextRect.top = 0;
        Rects.ContextRect.right = pAlloc->AllocData.SurfDesc.width;
        Rects.ContextRect.bottom = pAlloc->AllocData.SurfDesc.height;
        Rects.UpdateRects.cRects = 1;
        Rects.UpdateRects.aRects[0] = Rects.ContextRect;

        Status = vboxVdmaProcessVRegCmd(pDevExt, pContext, &pFlip->Flip.Alloc, &pFlip->Flip.Alloc, &SrcRect, &Rects);
        if (!NT_SUCCESS(Status))
            WARN(("vboxVdmaProcessVRegCmd failed Status 0x%x", Status));
    }
    else
#endif
    {
        WARN(("unexpected flip request"));
    }

    return Status;
}

NTSTATUS vboxVdmaProcessClrFillCmd(PVBOXMP_DEVEXT pDevExt, VBOXWDDM_CONTEXT *pContext, VBOXWDDM_DMA_PRIVATEDATA_CLRFILL *pCF)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PVBOXWDDM_ALLOCATION pAlloc = pCF->ClrFill.Alloc.pAlloc;
#ifdef VBOX_WITH_CROGL
    if (!pAlloc->AllocData.hostID)
#endif
    {
        Status = vboxVdmaGgDmaColorFill(pDevExt, &pCF->ClrFill);
        if (!NT_SUCCESS(Status))
            WARN(("vboxVdmaGgDmaColorFill failed Status 0x%x", Status));
    }
#ifdef VBOX_WITH_CROGL
    else
        WARN(("unexpected clrfill request"));
#endif

    return Status;
}

#ifdef VBOX_WITH_VDMA
/*
 * This is currently used by VDMA. It is invisible for Vdma API clients since
 * Vdma transport may change if we choose to use another (e.g. more light-weight)
 * transport for DMA commands submission
 */

#ifdef VBOXVDMA_WITH_VBVA
static int vboxWddmVdmaSubmitVbva(PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
{
    int rc;
    if (vboxVbvaBufferBeginUpdate (pDevExt, &pDevExt->u.primary.Vbva))
    {
        rc = vboxVbvaReportCmdOffset(pDevExt, &pDevExt->u.primary.Vbva, offDr);
        vboxVbvaBufferEndUpdate (pDevExt, &pDevExt->u.primary.Vbva);
    }
    else
    {
        AssertBreakpoint();
        rc = VERR_INVALID_STATE;
    }
    return rc;
}
#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitVbva
#else
static int vboxWddmVdmaSubmitHgsmi(PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
{
    VBoxVideoCmnPortWriteUlong(VBoxCommonFromDeviceExt(pDevExt)->guestCtx.port, offDr);
    return VINF_SUCCESS;
}
#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitHgsmi
#endif

static int vboxVdmaInformHost(PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo, VBOXVDMA_CTL_TYPE enmCtl)
{
    int rc = VINF_SUCCESS;

    PVBOXVDMA_CTL pCmd = (PVBOXVDMA_CTL)VBoxSHGSMICommandAlloc(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, sizeof (VBOXVDMA_CTL), HGSMI_CH_VBVA, VBVA_VDMA_CTL);
    if (pCmd)
    {
        pCmd->enmCtl = enmCtl;
        pCmd->u32Offset = pInfo->CmdHeap.Heap.area.offBase;
        pCmd->i32Result = VERR_NOT_SUPPORTED;

        const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pCmd);
        Assert(pHdr);
        if (pHdr)
        {
            do
            {
                HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
                Assert(offCmd != HGSMIOFFSET_VOID);
                if (offCmd != HGSMIOFFSET_VOID)
                {
                    rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
                    AssertRC(rc);
                    if (RT_SUCCESS(rc))
                    {
                        rc = VBoxSHGSMICommandDoneSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
                        AssertRC(rc);
                        if (RT_SUCCESS(rc))
                        {
                            rc = pCmd->i32Result;
                            AssertRC(rc);
                        }
                        break;
                    }
                }
                else
                    rc = VERR_INVALID_PARAMETER;
                /* fail to submit, cancel it */
                VBoxSHGSMICommandCancelSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
            } while (0);
        }

        VBoxSHGSMICommandFree (&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pCmd);
    }
    else
    {
        LOGREL(("HGSMIHeapAlloc failed"));
        rc = VERR_OUT_OF_RESOURCES;
    }

    return rc;
}
#endif

static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
{
    NOREF(pvEnv);
    return RTMemAlloc(cb);
}

static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
{
    NOREF(pvEnv);
    RTMemFree(pv);
}

static HGSMIENV g_hgsmiEnvVdma =
{
    NULL,
    hgsmiEnvAlloc,
    hgsmiEnvFree
};

/* create a DMACommand buffer */
int vboxVdmaCreate(PVBOXMP_DEVEXT pDevExt, VBOXVDMAINFO *pInfo
#ifdef VBOX_WITH_VDMA
        , ULONG offBuffer, ULONG cbBuffer
#endif
        )
{
    int rc;
    pInfo->fEnabled           = FALSE;

#ifdef VBOX_WITH_VDMA
    Assert((offBuffer & 0xfff) == 0);
    Assert((cbBuffer & 0xfff) == 0);
    Assert(offBuffer);
    Assert(cbBuffer);

    if((offBuffer & 0xfff)
            || (cbBuffer & 0xfff)
            || !offBuffer
            || !cbBuffer)
    {
        LOGREL(("invalid parameters: offBuffer(0x%x), cbBuffer(0x%x)", offBuffer, cbBuffer));
        return VERR_INVALID_PARAMETER;
    }
    PVOID pvBuffer;

    rc = VBoxMPCmnMapAdapterMemory(VBoxCommonFromDeviceExt(pDevExt),
                                   &pvBuffer,
                                   offBuffer,
                                   cbBuffer);
    Assert(RT_SUCCESS(rc));
    if (RT_SUCCESS(rc))
    {
        /* Setup a HGSMI heap within the adapter information area. */
        rc = VBoxSHGSMIInit(&pInfo->CmdHeap,
                             pvBuffer,
                             cbBuffer,
                             offBuffer,
                             &g_hgsmiEnvVdma);
        Assert(RT_SUCCESS(rc));
        if(RT_SUCCESS(rc))
#endif
        {
            return VINF_SUCCESS;
        }
#ifdef VBOX_WITH_VDMA
        else
            LOGREL(("HGSMIHeapSetup failed rc = 0x%x", rc));

        VBoxMPCmnUnmapAdapterMemory(VBoxCommonFromDeviceExt(pDevExt), &pvBuffer);
    }
    else
        LOGREL(("VBoxMapAdapterMemory failed rc = 0x%x\n", rc));
#endif
    return rc;
}

int vboxVdmaDisable (PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo)
{
    Assert(pInfo->fEnabled);
    if (!pInfo->fEnabled)
        return VINF_ALREADY_INITIALIZED;

    /* ensure nothing else is submitted */
    pInfo->fEnabled        = FALSE;
#ifdef VBOX_WITH_VDMA
    int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_DISABLE);
    AssertRC(rc);
    return rc;
#else
    return VINF_SUCCESS;
#endif
}

int vboxVdmaEnable (PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo)
{
    Assert(!pInfo->fEnabled);
    if (pInfo->fEnabled)
        return VINF_ALREADY_INITIALIZED;
#ifdef VBOX_WITH_VDMA
    int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_ENABLE);
    Assert(RT_SUCCESS(rc));
    if (RT_SUCCESS(rc))
        pInfo->fEnabled        = TRUE;

    return rc;
#else
    return VINF_SUCCESS;
#endif
}

#ifdef VBOX_WITH_VDMA
int vboxVdmaFlush (PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo)
{
    Assert(pInfo->fEnabled);
    if (!pInfo->fEnabled)
        return VINF_ALREADY_INITIALIZED;

    int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_FLUSH);
    Assert(RT_SUCCESS(rc));

    return rc;
}
#endif

int vboxVdmaDestroy (PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo)
{
    int rc = VINF_SUCCESS;
    Assert(!pInfo->fEnabled);
    if (pInfo->fEnabled)
        rc = vboxVdmaDisable (pDevExt, pInfo);
#ifdef VBOX_WITH_VDMA
    VBoxSHGSMITerm(&pInfo->CmdHeap);
    VBoxMPCmnUnmapAdapterMemory(VBoxCommonFromDeviceExt(pDevExt), (void**)&pInfo->CmdHeap.Heap.area.pu8Base);
#endif
    return rc;
}

#ifdef VBOX_WITH_VDMA
void vboxVdmaCBufDrFree (PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
{
    VBoxSHGSMICommandFree (&pInfo->CmdHeap, pDr);
}

PVBOXVDMACBUF_DR vboxVdmaCBufDrCreate (PVBOXVDMAINFO pInfo, uint32_t cbTrailingData)
{
    uint32_t cbDr = VBOXVDMACBUF_DR_SIZE(cbTrailingData);
    PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)VBoxSHGSMICommandAlloc (&pInfo->CmdHeap, cbDr, HGSMI_CH_VBVA, VBVA_VDMA_CMD);
    Assert(pDr);
    if (pDr)
        memset (pDr, 0, cbDr);
    else
        LOGREL(("VBoxSHGSMICommandAlloc returned NULL"));

    return pDr;
}

static DECLCALLBACK(void) vboxVdmaCBufDrCompletion(PVBOXSHGSMI pHeap, void *pvCmd, void *pvContext)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)pvContext;
    PVBOXVDMAINFO pInfo = &pDevExt->u.primary.Vdma;

    vboxVdmaCBufDrFree (pInfo, (PVBOXVDMACBUF_DR)pvCmd);
}

static DECLCALLBACK(PFNVBOXSHGSMICMDCOMPLETION) vboxVdmaCBufDrCompletionIrq(PVBOXSHGSMI pHeap, void *pvCmd, void *pvContext, void **ppvCompletion)
{
    PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)pvContext;
    PVBOXVDMAINFO pVdma = &pDevExt->u.primary.Vdma;
    PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)pvCmd;

    DXGK_INTERRUPT_TYPE enmComplType;

    if (RT_SUCCESS(pDr->rc))
    {
        enmComplType = DXGK_INTERRUPT_DMA_COMPLETED;
    }
    else if (pDr->rc == VERR_INTERRUPTED)
    {
        Assert(0);
        enmComplType = DXGK_INTERRUPT_DMA_PREEMPTED;
    }
    else
    {
        Assert(0);
        enmComplType = DXGK_INTERRUPT_DMA_FAULTED;
    }

    if (vboxVdmaDdiCmdCompletedIrq(pDevExt, VBOXVDMADDI_CMD_FROM_BUF_DR(pDr), enmComplType))
    {
        pDevExt->bNotifyDxDpc = TRUE;
    }

    /* inform SHGSMI we DO NOT want to be called at DPC later */
    return NULL;
//    *ppvCompletion = pvContext;
}

int vboxVdmaCBufDrSubmit(PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
{
    const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepAsynchIrq (&pInfo->CmdHeap, pDr, vboxVdmaCBufDrCompletionIrq, pDevExt, VBOXSHGSMI_FLAG_GH_ASYNCH_FORCE);
    Assert(pHdr);
    int rc = VERR_GENERAL_FAILURE;
    if (pHdr)
    {
        do
        {
            HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pInfo->CmdHeap, pHdr);
            Assert(offCmd != HGSMIOFFSET_VOID);
            if (offCmd != HGSMIOFFSET_VOID)
            {
                rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
                AssertRC(rc);
                if (RT_SUCCESS(rc))
                {
                    VBoxSHGSMICommandDoneAsynch(&pInfo->CmdHeap, pHdr);
                    AssertRC(rc);
                    break;
                }
            }
            else
                rc = VERR_INVALID_PARAMETER;
            /* fail to submit, cancel it */
            VBoxSHGSMICommandCancelAsynch(&pInfo->CmdHeap, pHdr);
        } while (0);
    }
    else
        rc = VERR_INVALID_PARAMETER;
    return rc;
}

int vboxVdmaCBufDrSubmitSynch(PVBOXMP_DEVEXT pDevExt, PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
{
    const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepAsynch (&pInfo->CmdHeap, pDr, NULL, NULL, VBOXSHGSMI_FLAG_GH_SYNCH);
    Assert(pHdr);
    int rc = VERR_GENERAL_FAILURE;
    if (pHdr)
    {
        do
        {
            HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pInfo->CmdHeap, pHdr);
            Assert(offCmd != HGSMIOFFSET_VOID);
            if (offCmd != HGSMIOFFSET_VOID)
            {
                rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
                AssertRC(rc);
                if (RT_SUCCESS(rc))
                {
                    VBoxSHGSMICommandDoneAsynch(&pInfo->CmdHeap, pHdr);
                    AssertRC(rc);
                    break;
                }
            }
            else
                rc = VERR_INVALID_PARAMETER;
            /* fail to submit, cancel it */
            VBoxSHGSMICommandCancelAsynch(&pInfo->CmdHeap, pHdr);
        } while (0);
    }
    else
        rc = VERR_INVALID_PARAMETER;
    return rc;
}
#endif

/* ddi dma command queue */

VOID vboxVdmaDdiCmdGetCompletedListIsr(PVBOXMP_DEVEXT pDevExt, LIST_ENTRY *pList)
{
    vboxVideoLeDetach(&pDevExt->DpcCmdQueue, pList);
}

BOOLEAN vboxVdmaDdiCmdIsCompletedListEmptyIsr(PVBOXMP_DEVEXT pDevExt)
{
    return IsListEmpty(&pDevExt->DpcCmdQueue);
}

DECLINLINE(BOOLEAN) vboxVdmaDdiCmdCanComplete(PVBOXMP_DEVEXT pDevExt, UINT u32NodeOrdinal)
{
    PVBOXVDMADDI_CMD_QUEUE pQueue = &pDevExt->aNodes[u32NodeOrdinal].CmdQueue;
    return ASMAtomicUoReadU32(&pQueue->cQueuedCmds) == 0;
}

DECLCALLBACK(VOID) vboxVdmaDdiCmdCompletionCbFree(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, PVOID pvContext)
{
    vboxWddmMemFree(pCmd);
}

static VOID vboxVdmaDdiCmdNotifyCompletedIrq(PVBOXMP_DEVEXT pDevExt, UINT u32NodeOrdinal, UINT u32FenceId, DXGK_INTERRUPT_TYPE enmComplType)
{
    PVBOXVDMADDI_NODE pNode = &pDevExt->aNodes[u32NodeOrdinal];
    DXGKARGCB_NOTIFY_INTERRUPT_DATA notify;
    memset(&notify, 0, sizeof(DXGKARGCB_NOTIFY_INTERRUPT_DATA));
    switch (enmComplType)
    {
        case DXGK_INTERRUPT_DMA_COMPLETED:
            notify.InterruptType = DXGK_INTERRUPT_DMA_COMPLETED;
            notify.DmaCompleted.SubmissionFenceId = u32FenceId;
            notify.DmaCompleted.NodeOrdinal = u32NodeOrdinal;
            pNode->uLastCompletedFenceId = u32FenceId;
            break;

        case DXGK_INTERRUPT_DMA_PREEMPTED:
            Assert(0);
            notify.InterruptType = DXGK_INTERRUPT_DMA_PREEMPTED;
            notify.DmaPreempted.PreemptionFenceId = u32FenceId;
            notify.DmaPreempted.NodeOrdinal = u32NodeOrdinal;
            notify.DmaPreempted.LastCompletedFenceId = pNode->uLastCompletedFenceId;
            break;

        case DXGK_INTERRUPT_DMA_FAULTED:
            Assert(0);
            notify.InterruptType = DXGK_INTERRUPT_DMA_FAULTED;
            notify.DmaFaulted.FaultedFenceId = u32FenceId;
            notify.DmaFaulted.Status = STATUS_UNSUCCESSFUL; /* @todo: better status ? */
            notify.DmaFaulted.NodeOrdinal = u32NodeOrdinal;
            break;

        default:
            Assert(0);
            break;
    }

    pDevExt->u.primary.DxgkInterface.DxgkCbNotifyInterrupt(pDevExt->u.primary.DxgkInterface.DeviceHandle, &notify);
}

static VOID vboxVdmaDdiCmdProcessCompletedIrq(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, DXGK_INTERRUPT_TYPE enmComplType)
{
    vboxVdmaDdiCmdNotifyCompletedIrq(pDevExt, pCmd->u32NodeOrdinal, pCmd->u32FenceId, enmComplType);
    switch (enmComplType)
    {
        case DXGK_INTERRUPT_DMA_COMPLETED:
            InsertTailList(&pDevExt->DpcCmdQueue, &pCmd->QueueEntry);
            break;
        default:
            AssertFailed();
            break;
    }
}

DECLINLINE(VOID) vboxVdmaDdiCmdDequeueIrq(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd)
{
    PVBOXVDMADDI_CMD_QUEUE pQueue = &pDevExt->aNodes[pCmd->u32NodeOrdinal].CmdQueue;
    ASMAtomicDecU32(&pQueue->cQueuedCmds);
    RemoveEntryList(&pCmd->QueueEntry);
}

DECLINLINE(VOID) vboxVdmaDdiCmdEnqueueIrq(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd)
{
    PVBOXVDMADDI_CMD_QUEUE pQueue = &pDevExt->aNodes[pCmd->u32NodeOrdinal].CmdQueue;
    ASMAtomicIncU32(&pQueue->cQueuedCmds);
    InsertTailList(&pQueue->CmdQueue, &pCmd->QueueEntry);
}

VOID vboxVdmaDdiNodesInit(PVBOXMP_DEVEXT pDevExt)
{
    for (UINT i = 0; i < RT_ELEMENTS(pDevExt->aNodes); ++i)
    {
        pDevExt->aNodes[i].uLastCompletedFenceId = 0;
        PVBOXVDMADDI_CMD_QUEUE pQueue = &pDevExt->aNodes[i].CmdQueue;
        pQueue->cQueuedCmds = 0;
        InitializeListHead(&pQueue->CmdQueue);
    }
    InitializeListHead(&pDevExt->DpcCmdQueue);
}

BOOLEAN vboxVdmaDdiCmdCompletedIrq(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, DXGK_INTERRUPT_TYPE enmComplType)
{
    if (VBOXVDMADDI_STATE_NOT_DX_CMD == pCmd->enmState)
    {
        InsertTailList(&pDevExt->DpcCmdQueue, &pCmd->QueueEntry);
        return FALSE;
    }

    PVBOXVDMADDI_CMD_QUEUE pQueue = &pDevExt->aNodes[pCmd->u32NodeOrdinal].CmdQueue;
    BOOLEAN bQueued = pCmd->enmState > VBOXVDMADDI_STATE_NOT_QUEUED;
    BOOLEAN bComplete = FALSE;
    Assert(!bQueued || pQueue->cQueuedCmds);
    Assert(!bQueued || !IsListEmpty(&pQueue->CmdQueue));
    pCmd->enmState = VBOXVDMADDI_STATE_COMPLETED;
    if (bQueued)
    {
        if (pQueue->CmdQueue.Flink == &pCmd->QueueEntry)
        {
            vboxVdmaDdiCmdDequeueIrq(pDevExt, pCmd);
            bComplete = TRUE;
        }
    }
    else if (IsListEmpty(&pQueue->CmdQueue))
    {
        bComplete = TRUE;
    }
    else
    {
        vboxVdmaDdiCmdEnqueueIrq(pDevExt, pCmd);
    }

    if (bComplete)
    {
        vboxVdmaDdiCmdProcessCompletedIrq(pDevExt, pCmd, enmComplType);

        while (!IsListEmpty(&pQueue->CmdQueue))
        {
            pCmd = VBOXVDMADDI_CMD_FROM_ENTRY(pQueue->CmdQueue.Flink);
            if (pCmd->enmState == VBOXVDMADDI_STATE_COMPLETED)
            {
                vboxVdmaDdiCmdDequeueIrq(pDevExt, pCmd);
                vboxVdmaDdiCmdProcessCompletedIrq(pDevExt, pCmd, pCmd->enmComplType);
            }
            else
                break;
        }
    }
    else
    {
        pCmd->enmState = VBOXVDMADDI_STATE_COMPLETED;
        pCmd->enmComplType = enmComplType;
    }

    return bComplete;
}

VOID vboxVdmaDdiCmdSubmittedIrq(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd)
{
    BOOLEAN bQueued = pCmd->enmState >= VBOXVDMADDI_STATE_PENDING;
    Assert(pCmd->enmState < VBOXVDMADDI_STATE_SUBMITTED);
    pCmd->enmState = VBOXVDMADDI_STATE_SUBMITTED;
    if (!bQueued)
        vboxVdmaDdiCmdEnqueueIrq(pDevExt, pCmd);
}

typedef struct VBOXVDMADDI_CMD_COMPLETED_CB
{
    PVBOXMP_DEVEXT pDevExt;
    PVBOXVDMADDI_CMD pCmd;
    DXGK_INTERRUPT_TYPE enmComplType;
} VBOXVDMADDI_CMD_COMPLETED_CB, *PVBOXVDMADDI_CMD_COMPLETED_CB;

static BOOLEAN vboxVdmaDdiCmdCompletedCb(PVOID Context)
{
    PVBOXVDMADDI_CMD_COMPLETED_CB pdc = (PVBOXVDMADDI_CMD_COMPLETED_CB)Context;
    PVBOXMP_DEVEXT pDevExt = pdc->pDevExt;
    BOOLEAN bNeedDpc = vboxVdmaDdiCmdCompletedIrq(pDevExt, pdc->pCmd, pdc->enmComplType);
    pDevExt->bNotifyDxDpc |= bNeedDpc;

    if (bNeedDpc)
    {
        pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
    }

    return bNeedDpc;
}

NTSTATUS vboxVdmaDdiCmdCompleted(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, DXGK_INTERRUPT_TYPE enmComplType)
{
    VBOXVDMADDI_CMD_COMPLETED_CB context;
    context.pDevExt = pDevExt;
    context.pCmd = pCmd;
    context.enmComplType = enmComplType;
    BOOLEAN bNeedDps;
    NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbSynchronizeExecution(
            pDevExt->u.primary.DxgkInterface.DeviceHandle,
            vboxVdmaDdiCmdCompletedCb,
            &context,
            0, /* IN ULONG MessageNumber */
            &bNeedDps);
    Assert(Status == STATUS_SUCCESS);
    return Status;
}

typedef struct VBOXVDMADDI_CMD_SUBMITTED_CB
{
    PVBOXMP_DEVEXT pDevExt;
    PVBOXVDMADDI_CMD pCmd;
} VBOXVDMADDI_CMD_SUBMITTED_CB, *PVBOXVDMADDI_CMD_SUBMITTED_CB;

static BOOLEAN vboxVdmaDdiCmdSubmittedCb(PVOID Context)
{
    PVBOXVDMADDI_CMD_SUBMITTED_CB pdc = (PVBOXVDMADDI_CMD_SUBMITTED_CB)Context;
    vboxVdmaDdiCmdSubmittedIrq(pdc->pDevExt, pdc->pCmd);

    return FALSE;
}

NTSTATUS vboxVdmaDdiCmdSubmitted(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd)
{
    VBOXVDMADDI_CMD_SUBMITTED_CB context;
    context.pDevExt = pDevExt;
    context.pCmd = pCmd;
    BOOLEAN bRc;
    NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbSynchronizeExecution(
            pDevExt->u.primary.DxgkInterface.DeviceHandle,
            vboxVdmaDdiCmdSubmittedCb,
            &context,
            0, /* IN ULONG MessageNumber */
            &bRc);
    Assert(Status == STATUS_SUCCESS);
    return Status;
}

typedef struct VBOXVDMADDI_CMD_COMPLETE_CB
{
    PVBOXMP_DEVEXT pDevExt;
    UINT u32NodeOrdinal;
    uint32_t u32FenceId;
} VBOXVDMADDI_CMD_COMPLETE_CB, *PVBOXVDMADDI_CMD_COMPLETE_CB;

static BOOLEAN vboxVdmaDdiCmdFenceCompleteCb(PVOID Context)
{
    PVBOXVDMADDI_CMD_COMPLETE_CB pdc = (PVBOXVDMADDI_CMD_COMPLETE_CB)Context;
    PVBOXMP_DEVEXT pDevExt = pdc->pDevExt;

    vboxVdmaDdiCmdNotifyCompletedIrq(pDevExt, pdc->u32NodeOrdinal, pdc->u32FenceId, DXGK_INTERRUPT_DMA_COMPLETED);

    pDevExt->bNotifyDxDpc = TRUE;
    pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);

    return TRUE;
}

static NTSTATUS vboxVdmaDdiCmdFenceNotifyComplete(PVBOXMP_DEVEXT pDevExt, uint32_t u32NodeOrdinal, uint32_t u32FenceId)
{
    VBOXVDMADDI_CMD_COMPLETE_CB context;
    context.pDevExt = pDevExt;
    context.u32NodeOrdinal = u32NodeOrdinal;
    context.u32FenceId = u32FenceId;
    BOOLEAN bRet;
    NTSTATUS Status = pDevExt->u.primary.DxgkInterface.DxgkCbSynchronizeExecution(
            pDevExt->u.primary.DxgkInterface.DeviceHandle,
            vboxVdmaDdiCmdFenceCompleteCb,
            &context,
            0, /* IN ULONG MessageNumber */
            &bRet);
    Assert(Status == STATUS_SUCCESS);
    return Status;
}

NTSTATUS vboxVdmaDdiCmdFenceComplete(PVBOXMP_DEVEXT pDevExt, uint32_t u32NodeOrdinal, uint32_t u32FenceId, DXGK_INTERRUPT_TYPE enmComplType)
{
    if (vboxVdmaDdiCmdCanComplete(pDevExt, u32NodeOrdinal))
        return vboxVdmaDdiCmdFenceNotifyComplete(pDevExt, u32NodeOrdinal, u32FenceId);

    PVBOXVDMADDI_CMD pCmd = (PVBOXVDMADDI_CMD)vboxWddmMemAlloc(sizeof (VBOXVDMADDI_CMD));
    Assert(pCmd);
    if (pCmd)
    {
        vboxVdmaDdiCmdInit(pCmd, u32NodeOrdinal, u32FenceId, vboxVdmaDdiCmdCompletionCbFree, NULL);
        NTSTATUS Status = vboxVdmaDdiCmdCompleted(pDevExt, pCmd, enmComplType);
        Assert(Status == STATUS_SUCCESS);
        if (Status == STATUS_SUCCESS)
            return STATUS_SUCCESS;
        vboxWddmMemFree(pCmd);
        return Status;
    }
    return STATUS_NO_MEMORY;
}


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<meta http-equiv="content-type" content="text/html;charset=utf-8">

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Plugins | </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="prev" href="settings/shortcut-mapper.html" />
<link rel="up" href="../notepad-online-document.html" />
<link rel="next" href="command-line.html" />
<link rel="shortcut icon" href="../../sites/default/files/acquia_marina_favicon.gif" type="image/x-icon" />
<meta name="keywords" content="Notepad++ Documentation,Notepad++,text editor" />
<meta name="description" content="

" />
    <link type="text/css" rel="stylesheet" media="all" href="../../modules/book/book5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../modules/node/node5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../modules/system/defaults5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../modules/system/system5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../modules/system/system-menus5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../modules/user/user5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/cck/theme/content-module5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/ckeditor/ckeditor5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/dhtml_menu/dhtml_menu5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/fancy_login/css/fancy_login5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/feedback/feedback5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/cck/modules/fieldgroup/fieldgroup5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/modules/views/css/views5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/style5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/icons5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/dblog5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/theme_settings/fixed5dbc.css?S" />
<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/theme_settings/fonts_15dbc.css?S" />

        <link type="text/css" rel="stylesheet" media="all" href="../../sites/all/themes/acquia_marina/local.css" />
    	<link type="text/css" rel="stylesheet" media="all" href="../../sites/all/css/home.css" />

          Skip to Main Content

Documentation

FAQ

Documentation Navigation

File management

Session management

Editing

Document Properties

Searching

Display

Macros

Commands

Shortcuts for the Run menu

Windows Dialog

GUI elements

Switching between Documents

Languages

Settings

Plugins

Command Line

Control files

Shell Extension

Further help

Upgrading

Credits

Plugins

	Notepad++ is very extensible using so called plugins. Plugins are small or big additions to Notepad++ to enhance its functionality. Notepad++ comes bundled with a few plugins (when using the installer, you can choose which ones to add), but you can always add your own or remove some. The plugins are located in the Plugins directory in the main Notepad++ installation directory. They are DLL files and simply removing or adding them is enough.

	When Notepad++ starts, it looks into its Plugins configuration folder and loads whatever dll it finds. Later, you can add plugins using the Settings -> Import -> Import plugins menu. Be careful to make sure your version of Notepad++ is up-to-date enough for new plugins and that the plugin is compatible with Notepad++ (some very old plugins will not work with newer versions of Notepad++, it is up to the developer of the plugin to add support or not).

	Currently, Notepad++ can be found in an (older) ANSI version and the newer Unicode version. The plugin has to match this version, otherwise it will not work (Notepad++ will warn you about this). Plugins can be found anywhere on the internet, but a large can be found in the Notepad++ Plugins project on SourceForge.net: http://sourceforge.net/projects/npp-plugins/. There is even a plugin, called Plugin Manager, which works out the installation details and version management for you.

	If a plugin misbehaves, Notepad++ will go out of its way to prevent the failure from propagating so as to avoid any loss of data. In such a case, you will be presented with information about the plugin reporting a problem. Making this information available on the Plugin Development forum will help removing the cause of the problem as soon as possible.

              ‹ Shortcut Mapper
                    up
                    Command Line ›

            Login

X<form id="user-login" method="post" accept-charset="UTF-8" action="http://npp-community.tuxfamily.org/user/login">

 Username: *
 <input type="text" maxlength="60" name="name" id="edit-name" size="15" value="" tabindex="1" class="form-text required" />

 Password: *
 <input type="password" name="pass" id="edit-pass"  maxlength="128"  size="15"  tabindex="2" class="form-text required" />

<input type="hidden" name="form_id" id="edit-user-login" value="user_login"  />
<input type="hidden" name="form_build_id" id="form-9ff4ac00baaf203187ca367462525d17" value="form-9ff4ac00baaf203187ca367462525d17"  />
<input type="submit" name="op" id="edit-submit" value="Log in"  tabindex="3" class="form-submit" />

</form>

Create new account

Request new password

Feedback

            <form action="http://npp-community.tuxfamily.org/documentation/notepad-user-manual/plugins"  accept-charset="UTF-8" method="post" id="feedback-form" class="feedback-form">

<input type="hidden" name="ajax" id="edit-ajax" value="0"  />

If you experience a bug or would like to see an addition on the current page, feel free to leave us a message.

 Message: *
 <textarea cols="60" rows="5" name="message" id="edit-message"  class="form-textarea resizable required feedback-message"></textarea>

<input type="submit" name="op" id="feedback-submit" value="Send"  class="form-submit" />

<input type="hidden" name="form_build_id" id="form-06455b321011dd9a01da63b72daa2122" value="form-06455b321011dd9a01da63b72daa2122"  />
<input type="hidden" name="form_id" id="edit-feedback-form" value="feedback_form"  />

</form>

<meta http-equiv="content-type" content="text/html;charset=utf-8">

redpajama_set_name: RedPajamaGithub


text:
;; cycle-placement -- toggle a window's placement between multiple candidate
;; positions.
;;
;; Copyright 2014 Cory Echols <cory@sixtimesnine.org>

(define-structure cycle-placement
	(export
		cycle-window-placement-on-current-head
		move-resize-frame-by-percent
		move-resize-frame-by-percent-on-current-head
	)

	(open rep
		rep.system
		sawfish.wm.state.maximize
		sawfish.wm.windows
		sawfish.wm.misc
		sawfish.wm.util.rects
	)

	;; Standard reduce thing
	(defun reduce (f seq start)
		(cond
			((equal (length seq) 0) start)
			(t (reduce f (cdr seq) (funcall f start (car seq))))
		)
	)

	;; Find dock windows on the current window's head.
	(defun docks-on-current-head (win)
		(filter-windows (lambda (w)
			(and
				(equal (current-head win) (current-head w))
				(window-get w 'dock-type))))
	)

	;; Return a new size for window w, so that it will not overlap any dock
	;; windows within the rectangle defined by (x y width height)
	(defun bump-window-position-for-docks (win x y width height)
		(let* ((win-rect (list x y (+ x width) (+ y height)))
			   (largest-rect (reduce
							   find-largest-subrect
							   (rectangles-from-windows (docks-on-current-head (input-focus)))
							   win-rect)
			   )
			   (left (nth 0 largest-rect))
			   (top (nth 1 largest-rect))
			   (width (- (nth 2 largest-rect) left))
			   (height (- (nth 3 largest-rect) top))
			  )
			(list left top width height)
		)
	)

	(defun max-by-weight-func-helper (f seq max-weight max-elem)
		(cond
			((equal (length seq) 0) max-elem)
			((> max-weight (funcall f (car seq))) (max-by-weight-func-helper f (cdr seq) max-weight max-elem))
			(t (max-by-weight-func-helper f (cdr seq) (funcall f (car seq)) (car seq)))
		)
	)

	;; Apply f to each elem in seq.  Return the elem in seq that results in the
	;; max value for f.
	(defun max-by-weight-func (f seq)
		(cond
			((equal (length seq) 0) seq)
			(t (max-by-weight-func-helper f (cdr seq) (funcall f (car seq)) (car seq)))
		)
	)

	;; Find the largest (by area) rectangle that would result from
	;; removing r1 from r2
	(defun find-largest-subrect (r1 r2)
		(max-by-weight-func rectangle-area (rect-minus r1 r2))
	)

	;; Move and resize the frame to the supplied position and size
	(defun move-resize-frame-to (window x y width height)
		(move-window-to window x y)
		(resize-frame-to window width height)
	)

	;; Resize the window's frame to the provided width and height.
	(defun resize-frame-to (window width height)
		(let* ((dim-difference (window-frame-difference window))
			   (w-diff (car dim-difference))
			   (h-diff (cdr dim-difference))
			   (new-width (- width w-diff))
			   (new-height (- height h-diff)))
			(unmaximize-window window)
			(resize-window-to window new-width new-height))
	)

	;; Calculate how much frame decorations add to a window's size.
	(defun window-frame-difference (window)
		(let* ((win-dim (window-dimensions window))
			  (win-w (car win-dim))
			  (win-h (cdr win-dim))
			  (frame-dim (window-frame-dimensions window))
			  (frame-w (car frame-dim))
			  (frame-h (cdr frame-dim)))
			(cons (- frame-w win-w) (- frame-h win-h)))
	)

	;; Move and resize a frame, where supplied coordinates and size are
	;; a percentage of the current screen dimensions.
	;; Also avoid any docks or panels within (x y width height)
	(defun move-resize-frame-by-percent (window x y width height)
		(let* ((screen-x (car (screen-dimensions)))
			   (screen-y (cdr (screen-dimensions)))
			   (new-x (exact-percent x screen-x))
			   (new-y (exact-percent y screen-y))
			   (new-width (exact-percent width screen-x))
			   (new-height (exact-percent height screen-y))
			   (bumped-position (bump-window-position-for-docks window new-x new-y new-width new-height))
			  )
			(move-resize-frame-to
			  window
			  (nth 0 bumped-position)
			  (nth 1 bumped-position)
			  (nth 2 bumped-position)
			  (nth 3 bumped-position)
			))
	)

	;; Move and resize a frame, where suppplied coordinates and size are a
	;; percentage of the current monitor dimensions
	(defun move-resize-frame-by-percent-on-current-head (window x y width height)
		(let* ((head-x (car (current-head-dimensions window)))
			   (head-y (cdr (current-head-dimensions window)))
			   (head-offset-x (car (current-head-offset window)))
			   (head-offset-y (cdr (current-head-offset window)))
			   (new-x (+ head-offset-x (exact-percent x head-x)))
			   (new-y (+ head-offset-y (exact-percent y head-y)))
			   (new-width (exact-percent width head-x))
			   (new-height (exact-percent height head-y))
			   (bumped-position (bump-window-position-for-docks window new-x new-y new-width new-height))
			  )
			(move-resize-frame-to
			  window
			  (nth 0 bumped-position)
			  (nth 1 bumped-position)
			  (nth 2 bumped-position)
			  (nth 3 bumped-position)
			)
		)
	)

	;; Return 'percent' of 'quantity' as an exact integer.
	;; librep has muliple numeric types, but it looks like sawfish wants
	;; integers for everything.  There's probably a better way to do this.
	(defun exact-percent (percent quantity)
		(inexact->exact (exact->inexact (* ( / percent 100) quantity) ))
	)

	;; Cycle window through the list of candidate positions
	;; _window_ is a window, as would be returned by sawfish functions such as
	;; `(input-focus)`.
    ;;
	;; _list-of-candidates_ is a list-of-lists.  Each of the contained lists is
	;; a candidate position.  The candidate position describes where the window
	;; should be placed as `(x-percent y-percent width-percent height-percent)`.
	;; The first invocation of the function will place the window according to
	;; the first entry in `list-of-candidates`.  Successive invocations will
	;; select from the following entries in the list.
	(defun cycle-window-placement-on-current-head (window candidate-positions)
		(let* ((last-cycled-position (window-get window 'cte-last-cycled-pos 'none))
			   (last-cycled-member (member last-cycled-position candidate-positions))
			   (next-cycled-position (cond
										((< 1 (length last-cycled-member))
											(nth 1 last-cycled-member))
										(t
											(nth 0 candidate-positions)))))
			(window-put window 'cte-last-cycled-pos next-cycled-position)
			(move-resize-frame-by-percent-on-current-head
				window
				(nth 0 next-cycled-position)
				(nth 1 next-cycled-position)
				(nth 2 next-cycled-position)
				(nth 3 next-cycled-position)
			)
		)
	)
)


text:
<?php

namespace {{ namespace }}\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Bike
 *
 * @ORM\Table(name="{{ prefix }}bike")
 * @ORM\Entity
 */
class Bike extends \Kunstmaan\AdminBundle\Entity\AbstractEntity
{
    const TYPE_CITY_BIKE = 'city_bike';
    const TYPE_MOUNTAIN_BIKE = 'mountain_bike';
    const TYPE_RACING_BIKE = 'racing_bike';

    /**
     * @var array Supported bike types
     */
    public static $types = array(
        self::TYPE_CITY_BIKE,
        self::TYPE_MOUNTAIN_BIKE,
        self::TYPE_RACING_BIKE
    );

    /**
     * @var string
     *
     * @ORM\Column(name="type", type="string", length=20, nullable=true)
     * @Assert\NotBlank()
     */
    private $type;

    /**
     * @var string
     *
     * @ORM\Column(name="model", type="string", length=100, nullable=true)
     * @Assert\NotBlank()
     */
    private $model;

    /**
     * @var string
     *
     * @ORM\Column(name="brand", type="string", length=100, nullable=true)
     * @Assert\NotBlank()
     */
    private $brand;

    /**
     * @var double
     *
     * @ORM\Column(name="price", type="decimal", precision=8, scale=2, nullable=true)
     */
    private $price;

    /**
     * @ORM\Column(type="smallint", nullable=true)
     * @Assert\Type(type = "numeric")
     *
     * @var integer
     */
    private $weight;

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param string $type
     *
     * @return Bike
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * @return string
     */
    public function getModel()
    {
        return $this->model;
    }

    /**
     * @param string $model
     *
     * @return Bike
     */
    public function setModel($model)
    {
        $this->model = $model;

        return $this;
    }

    /**
     * @return string
     */
    public function getBrand()
    {
        return $this->brand;
    }

    /**
     * @param string $brand
     *
     * @return Bike
     */
    public function setBrand($brand)
    {
        $this->brand = $brand;

        return $this;
    }

    /**
     * @return float
     */
    public function getPrice()
    {
        return $this->price;
    }

    /**
     * @param float $price
     *
     * @return Bike
     */
    public function setPrice($price)
    {
        $this->price = $price;

        return $this;
    }

    /**
     * Set weight
     *
     * @param integer $weight
     *
     * @return Bike
     */
    public function setWeight($weight)
    {
        $this->weight = $weight;

        return $this;
    }

    /**
     * Get weight
     *
     * @return integer
     */
    public function getWeight()
    {
        return $this->weight;
    }
}


text:
<?php
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: google/ads/googleads/v11/enums/search_term_targeting_status.proto

namespace Google\Ads\GoogleAds\V11\Enums\SearchTermTargetingStatusEnum;

use UnexpectedValueException;

/**
 * Indicates whether the search term is one of your targeted or excluded
 * keywords.
 *
 * Protobuf type google.ads.googleads.v11.enums.SearchTermTargetingStatusEnum.SearchTermTargetingStatus
 */
class SearchTermTargetingStatus
{
    /**
     * Not specified.
     *
     * Generated from protobuf enum UNSPECIFIED = 0;
     */
    const UNSPECIFIED = 0;
    /**
     * Used for return value only. Represents value unknown in this version.
     *
     * Generated from protobuf enum UNKNOWN = 1;
     */
    const UNKNOWN = 1;
    /**
     * Search term is added to targeted keywords.
     *
     * Generated from protobuf enum ADDED = 2;
     */
    const ADDED = 2;
    /**
     * Search term matches a negative keyword.
     *
     * Generated from protobuf enum EXCLUDED = 3;
     */
    const EXCLUDED = 3;
    /**
     * Search term has been both added and excluded.
     *
     * Generated from protobuf enum ADDED_EXCLUDED = 4;
     */
    const ADDED_EXCLUDED = 4;
    /**
     * Search term is neither targeted nor excluded.
     *
     * Generated from protobuf enum NONE = 5;
     */
    const NONE = 5;

    private static $valueToName = [
        self::UNSPECIFIED => 'UNSPECIFIED',
        self::UNKNOWN => 'UNKNOWN',
        self::ADDED => 'ADDED',
        self::EXCLUDED => 'EXCLUDED',
        self::ADDED_EXCLUDED => 'ADDED_EXCLUDED',
        self::NONE => 'NONE',
    ];

    public static function name($value)
    {
        if (!isset(self::$valueToName[$value])) {
            throw new UnexpectedValueException(sprintf(
                    'Enum %s has no name defined for value %s', __CLASS__, $value));
        }
        return self::$valueToName[$value];
    }

    public static function value($name)
    {
        $const = __CLASS__ . '::' . strtoupper($name);
        if (!defined($const)) {
            throw new UnexpectedValueException(sprintf(
                    'Enum %s has no value defined for name %s', __CLASS__, $name));
        }
        return constant($const);
    }
}

// Adding a class alias for backwards compatibility with the previous class name.
class_alias(SearchTermTargetingStatus::class, \Google\Ads\GoogleAds\V11\Enums\SearchTermTargetingStatusEnum_SearchTermTargetingStatus::class);


text:
<?php

/**
 * ICreateDatabaseStatement interface file.
 *
 * @author Anastaszor
 */
interface ICreateDatabaseStatement extends IExecutableStatement
{

	/**
	 * Returns true if the IF NOT EXISTS clause is present
	 *
	 * @return boolean
	 */
	public function getIfNotExists();

	/**
	 * Gets the name of the database (not escaped)
	 *
	 * @return string
	 */
	public function getDatabaseName();

	/**
	 * Gets the specifications for the creation of the database.
	 *
	 * @return IDatabaseSpecificationStatement[]
	 */
	public function getSpecifications();

}


text: docker stats $(docker ps --format={{.Names}}) --no-stream > /tmp/dockerStats.out