/* $Id: server_presenter.cpp $ */

/** @file
 * Presenter API
 */

/*
 * Copyright (C) 2012-2013 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 "cr_spu.h"
#include "chromium.h"
#include "cr_error.h"
#include "cr_net.h"
#include "cr_rand.h"
#include "server_dispatch.h"
#include "server.h"
#include "cr_mem.h"
#include "cr_string.h"
#include <cr_vreg.h>
#include <cr_htable.h>
#include <cr_bmpscale.h>

#include <iprt/cdefs.h>
#include <iprt/types.h>
#include <iprt/asm.h>
#include <iprt/mem.h>
#include <iprt/list.h>


#ifdef DEBUG_misha
# define VBOXVDBG_MEMCACHE_DISABLE
#endif

#ifndef VBOXVDBG_MEMCACHE_DISABLE
# include <iprt/memcache.h>
#endif

#include "render/renderspu.h"

//#define CR_SERVER_WITH_CLIENT_CALLOUTS

class ICrFbDisplay
{
public:
    virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) = 0;
    virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) = 0;

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
    virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) = 0;
    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
    virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;
    virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0;

    virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) = 0;

    virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) = 0;

    virtual ~ICrFbDisplay() {}
};

class CrFbDisplayComposite;
class CrFbDisplayBase;
class CrFbDisplayWindow;
class CrFbDisplayWindowRootVr;
class CrFbDisplayVrdp;
class CrFbWindow;

typedef struct CR_FRAMEBUFFER
{
    VBOXVR_SCR_COMPOSITOR Compositor;
    struct VBVAINFOSCREEN ScreenInfo;
    void *pvVram;
    ICrFbDisplay *pDisplay;
    RTLISTNODE EntriesList;
    uint32_t cEntries; /* <- just for debugging */
    uint32_t cUpdating;
    CRHTABLE SlotTable;
} CR_FRAMEBUFFER;

typedef union CR_FBENTRY_FLAGS
{
    struct {
        uint32_t fCreateNotified : 1;
        uint32_t fInList         : 1;
        uint32_t Reserved        : 30;
    };
    uint32_t Value;
} CR_FBENTRY_FLAGS;

typedef struct CR_FRAMEBUFFER_ENTRY
{
    VBOXVR_SCR_COMPOSITOR_ENTRY Entry;
    RTLISTNODE Node;
    uint32_t cRefs;
    CR_FBENTRY_FLAGS Flags;
    CRHTABLE HTable;
} CR_FRAMEBUFFER_ENTRY;

typedef struct CR_FBTEX
{
    CR_TEXDATA Tex;
    CRTextureObj *pTobj;
} CR_FBTEX;

#define PCR_FBTEX_FROM_TEX(_pTex) ((CR_FBTEX*)((uint8_t*)(_pTex) - RT_OFFSETOF(CR_FBTEX, Tex)))
#define PCR_FRAMEBUFFER_FROM_COMPOSITOR(_pCompositor) ((CR_FRAMEBUFFER*)((uint8_t*)(_pCompositor) - RT_OFFSETOF(CR_FRAMEBUFFER, Compositor)))
#define PCR_FBENTRY_FROM_ENTRY(_pEntry) ((CR_FRAMEBUFFER_ENTRY*)((uint8_t*)(_pEntry) - RT_OFFSETOF(CR_FRAMEBUFFER_ENTRY, Entry)))

typedef struct CR_FB_INFO
{
    CrFbDisplayComposite *pDpComposite;
    uint32_t u32Id;
    VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
} CR_FB_INFO;

typedef struct CR_FBDISPLAY_INFO
{
    CrFbDisplayWindow *pDpWin;
    CrFbDisplayWindowRootVr *pDpWinRootVr;
    CrFbDisplayVrdp *pDpVrdp;
    CrFbWindow *pWindow;
    uint32_t u32DisplayMode;
    uint32_t u32Id;
    int32_t iFb;
} CR_FBDISPLAY_INFO;

typedef struct CR_PRESENTER_GLOBALS
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
    RTMEMCACHE FbEntryLookasideList;
    RTMEMCACHE FbTexLookasideList;
    RTMEMCACHE CEntryLookasideList;
#endif
    uint32_t u32DisplayMode;
    uint32_t u32DisabledDisplayMode;
    bool fEnabled;
    CRHashTable *pFbTexMap;
    CR_FBDISPLAY_INFO aDisplayInfos[CR_MAX_GUEST_MONITORS];
    CR_FBMAP FramebufferInitMap;
    CR_FRAMEBUFFER aFramebuffers[CR_MAX_GUEST_MONITORS];
    CR_FB_INFO aFbInfos[CR_MAX_GUEST_MONITORS];
    bool fWindowsForceHidden;
    uint32_t cbTmpBuf;
    void *pvTmpBuf;
    uint32_t cbTmpBuf2;
    void *pvTmpBuf2;
} CR_PRESENTER_GLOBALS;

static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd);

static CR_PRESENTER_GLOBALS g_CrPresenter;

/* FRAMEBUFFER */

void CrFbInit(CR_FRAMEBUFFER *pFb, uint32_t idFb)
{
    RTRECT Rect;
    Rect.xLeft = 0;
    Rect.yTop = 0;
    Rect.xRight = 1;
    Rect.yBottom = 1;
    memset(pFb, 0, sizeof (*pFb));
    pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED;
    pFb->ScreenInfo.u32ViewIndex = idFb;
    CrVrScrCompositorInit(&pFb->Compositor, &Rect);
    RTListInit(&pFb->EntriesList);
    CrHTableCreate(&pFb->SlotTable, 0);
}

bool CrFbIsEnabled(CR_FRAMEBUFFER *pFb)
{
    return !(pFb->ScreenInfo.u16Flags & VBVA_SCREEN_F_DISABLED);
}

HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry);

const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(CR_FRAMEBUFFER *pFb)
{
    return &pFb->Compositor;
}

DECLINLINE(CR_FRAMEBUFFER*) CrFbFromCompositor(const struct VBOXVR_SCR_COMPOSITOR* pCompositor)
{
    return RT_FROM_MEMBER(pCompositor, CR_FRAMEBUFFER, Compositor);
}

const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb)
{
    return &hFb->ScreenInfo;
}

void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb)
{
    return hFb->pvVram;
}

int CrFbUpdateBegin(CR_FRAMEBUFFER *pFb)
{
    ++pFb->cUpdating;

    if (pFb->cUpdating == 1)
    {
        if (pFb->pDisplay)
            pFb->pDisplay->UpdateBegin(pFb);
    }

    return VINF_SUCCESS;
}

void CrFbUpdateEnd(CR_FRAMEBUFFER *pFb)
{
    if (!pFb->cUpdating)
    {
        WARN(("invalid UpdateEnd call!"));
        return;
    }

    --pFb->cUpdating;

    if (!pFb->cUpdating)
    {
        if (pFb->pDisplay)
            pFb->pDisplay->UpdateEnd(pFb);
    }
}

bool CrFbIsUpdating(const CR_FRAMEBUFFER *pFb)
{
    return !!pFb->cUpdating;
}

bool CrFbHas3DData(HCR_FRAMEBUFFER hFb)
{
    return !CrVrScrCompositorIsEmpty(&hFb->Compositor);
}

static void crFbImgFromScreenVram(const VBVAINFOSCREEN *pScreen, void *pvVram, CR_BLITTER_IMG *pImg)
{
    pImg->pvData = pvVram;
    pImg->cbData = pScreen->u32LineSize * pScreen->u32Height;
    pImg->enmFormat = GL_BGRA;
    pImg->width = pScreen->u32Width;
    pImg->height = pScreen->u32Height;
    pImg->bpp = pScreen->u16BitsPerPixel;
    pImg->pitch = pScreen->u32LineSize;
}

static void crFbImgFromDimPtrBGRA(void *pvVram, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg)
{
    pImg->pvData = pvVram;
    pImg->cbData = width * height * 4;
    pImg->enmFormat = GL_BGRA;
    pImg->width = width;
    pImg->height = height;
    pImg->bpp = 32;
    pImg->pitch = width * 4;
}

static int8_t crFbImgFromDimOffVramBGRA(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg)
{
    uint32_t cbBuff = width * height * 4;
    if (offVRAM >= g_cbVRam
            || offVRAM + cbBuff >= g_cbVRam)
    {
        WARN(("invalid param"));
        return -1;
    }

    uint8_t *pu8Buf = g_pvVRamBase + offVRAM;
    crFbImgFromDimPtrBGRA(pu8Buf, width, height, pImg);

    return 0;
}

static int8_t crFbImgFromDescBGRA(const VBOXCMDVBVA_ALLOCDESC *pDesc, CR_BLITTER_IMG *pImg)
{
    return crFbImgFromDimOffVramBGRA(pDesc->Info.u.offVRAM, pDesc->u16Width, pDesc->u16Height, pImg);
}

static void crFbImgFromFb(HCR_FRAMEBUFFER hFb, CR_BLITTER_IMG *pImg)
{
    const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
    void *pvVram = CrFbGetVRAM(hFb);
    crFbImgFromScreenVram(pScreen, pvVram, pImg);
}

static int crFbTexDataGetContents(CR_TEXDATA *pTex, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
{
    const CR_BLITTER_IMG *pSrcImg;
    int rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrTdBltDataAcquire failed rc %d", rc));
        return rc;
    }

    CrMBltImg(pSrcImg, pPos, cRects, pRects, pDst);

    CrTdBltDataRelease(pTex);

    return VINF_SUCCESS;
}

static int crFbBltGetContentsScaledDirect(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
{
    VBOXVR_LIST List;
    uint32_t c2DRects = 0;
    CR_TEXDATA *pEnteredTex = NULL;
    PCR_BLITTER pEnteredBlitter = NULL;
    uint32_t width = 0, height = 0;
    RTPOINT ScaledEntryPoint = {0};
    RTRECT ScaledSrcRect = {0};

    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
    int32_t srcWidth = pSrcRectSize->cx;
    int32_t srcHeight = pSrcRectSize->cy;
    int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
    int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;

    RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop};
    float strX = ((float)dstWidth) / srcWidth;
    float strY = ((float)dstHeight) / srcHeight;
    bool fScale = (dstWidth != srcWidth || dstHeight != srcHeight);
    Assert(fScale);

    VBoxVrListInit(&List);
    int rc = VBoxVrListRectsAdd(&List, 1, CrVrScrCompositorRectGet(&hFb->Compositor), NULL);
    if (!RT_SUCCESS(rc))
    {
        WARN(("VBoxVrListRectsAdd failed rc %d", rc));
        goto end;
    }

    CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter);

    for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter);
            pEntry;
            pEntry = CrVrScrCompositorConstIterNext(&Iter))
    {
        uint32_t cRegions;
        const RTRECT *pRegions;
        rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
            goto end;
        }

        rc = VBoxVrListRectsSubst(&List, cRegions, pRegions, NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxVrListRectsSubst failed rc %d", rc));
            goto end;
        }

        for (uint32_t j = 0; j < cRegions; ++j)
        {
            /* rects are in dst coordinates,
             * while the pReg is in source coords
             * convert */
            const RTRECT * pReg = &pRegions[j];
            RTRECT ScaledReg;
            /* scale */
            VBoxRectScaled(pReg, strX, strY, &ScaledReg);
            /* translate */
            VBoxRectTranslate(&ScaledReg, pDstRect->xLeft, pDstRect->yTop);

            for (uint32_t i = 0; i < cRects; ++i)
            {
                const RTRECT * pRect = &pRects[i];

                RTRECT Intersection;
                VBoxRectIntersected(pRect, &ScaledReg, &Intersection);
                if (VBoxRectIsZero(&Intersection))
                    continue;

                CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
                const CR_BLITTER_IMG *pSrcImg;

                if (pEnteredTex != pTex)
                {
                    if (!pEnteredBlitter)
                    {
                        pEnteredBlitter = CrTdBlitterGet(pTex);
                        rc = CrBltEnter(pEnteredBlitter);
                        if (!RT_SUCCESS(rc))
                        {
                            WARN(("CrBltEnter failed %d", rc));
                            pEnteredBlitter = NULL;
                            goto end;
                        }
                    }

                    if (pEnteredTex)
                    {
                        CrTdBltLeave(pEnteredTex);

                        pEnteredTex = NULL;

                        if (pEnteredBlitter != CrTdBlitterGet(pTex))
                        {
                            WARN(("blitters not equal!"));
                            CrBltLeave(pEnteredBlitter);

                            pEnteredBlitter = CrTdBlitterGet(pTex);
                            rc = CrBltEnter(pEnteredBlitter);
                             if (!RT_SUCCESS(rc))
                             {
                                 WARN(("CrBltEnter failed %d", rc));
                                 pEnteredBlitter = NULL;
                                 goto end;
                             }
                        }
                    }

                    rc = CrTdBltEnter(pTex);
                    if (!RT_SUCCESS(rc))
                    {
                        WARN(("CrTdBltEnter failed %d", rc));
                        goto end;
                    }

                    pEnteredTex = pTex;

                    const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex);

                    width = CR_FLOAT_RCAST(uint32_t, strX * pVrTex->width);
                    height = CR_FLOAT_RCAST(uint32_t, strY * pVrTex->height);
                    ScaledEntryPoint.x = CR_FLOAT_RCAST(int32_t, strX * CrVrScrCompositorEntryRectGet(pEntry)->xLeft) + pDstRect->xLeft;
                    ScaledEntryPoint.y = CR_FLOAT_RCAST(int32_t, strY * CrVrScrCompositorEntryRectGet(pEntry)->yTop) + pDstRect->yTop;
                    ScaledSrcRect.xLeft = ScaledEntryPoint.x;
                    ScaledSrcRect.yTop = ScaledEntryPoint.y;
                    ScaledSrcRect.xRight = width + ScaledEntryPoint.x;
                    ScaledSrcRect.yBottom = height + ScaledEntryPoint.y;
                }

                VBoxRectIntersect(&Intersection, &ScaledSrcRect);
                if (VBoxRectIsZero(&Intersection))
                    continue;

                rc = CrTdBltDataAcquireScaled(pTex, GL_BGRA, false, width, height, &pSrcImg);
                if (!RT_SUCCESS(rc))
                {
                    WARN(("CrTdBltDataAcquire failed rc %d", rc));
                    goto end;
                }

                bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS);

                CrMBltImgRect(pSrcImg, &ScaledEntryPoint, fInvert, &Intersection, pDst);

                CrTdBltDataReleaseScaled(pTex, pSrcImg);
            }
        }
    }

    c2DRects = VBoxVrListRectsCount(&List);
    if (c2DRects)
    {
        if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT))
        {
            if (g_CrPresenter.pvTmpBuf2)
                RTMemFree(g_CrPresenter.pvTmpBuf2);

            g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT);
            g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2);
            if (!g_CrPresenter.pvTmpBuf2)
            {
                WARN(("RTMemAlloc failed!"));
                g_CrPresenter.cbTmpBuf2 = 0;
                rc = VERR_NO_MEMORY;
                goto end;
            }
        }

        RTRECT *p2DRects  = (RTRECT *)g_CrPresenter.pvTmpBuf2;

        rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxVrListRectsGet failed, rc %d", rc));
            goto end;
        }

        const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor);

        CR_BLITTER_IMG FbImg;

        crFbImgFromFb(hFb, &FbImg);

        for (uint32_t i = 0; i < c2DRects; ++i)
        {
            VBoxRectScale(&p2DRects[i], strX, strY);
        }

        CrMBltImgScaled(&FbImg, pSrcRectSize, pDstRect, c2DRects, p2DRects, pDst);
    }

end:

    if (pEnteredTex)
        CrTdBltLeave(pEnteredTex);

    if (pEnteredBlitter)
        CrBltLeave(pEnteredBlitter);

    VBoxVrListClear(&List);

    return rc;
}

static int crFbBltGetContentsScaledCPU(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
{
    WARN(("not implemented!"));
    return VERR_NOT_IMPLEMENTED;
#if 0
    int32_t srcWidth = pSrcRectSize->cx;
    int32_t srcHeight = pSrcRectSize->cy;
    int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
    int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;

    RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop};
    float strX = ((float)dstWidth) / srcWidth;
    float strY = ((float)dstHeight) / srcHeight;

    RTPOINT UnscaledPos;
    UnscaledPos.x = CR_FLOAT_RCAST(int32_t, pDstRect->xLeft / strX);
    UnscaledPos.y = CR_FLOAT_RCAST(int32_t, pDstRect->yTop / strY);

    /* destination is bigger than the source, do 3D data stretching with CPU */
    CR_BLITTER_IMG Img;
    Img.cbData = srcWidth * srcHeight * 4;
    Img.pvData = RTMemAlloc(Img.cbData);
    if (!Img.pvData)
    {
        WARN(("RTMemAlloc Failed"));
        return VERR_NO_MEMORY;
    }
    Img.enmFormat = pImg->enmFormat;
    Img.width = srcWidth;
    Img.height = srcHeight;
    Img.bpp = pImg->bpp;
    Img.pitch = Img.width * 4;

    int rc = CrFbBltGetContents(hFb, &UnscaledPos, cRects, pRects, &Img);
    if (RT_SUCCESS(rc))
    {
        CrBmpScale32((uint8_t *)pImg->pvData,
                            pImg->pitch,
                            pImg->width, pImg->height,
                            (const uint8_t *)Img.pvData,
                            Img.pitch,
                            Img.width, Img.height);
    }
    else
        WARN(("CrFbBltGetContents failed %d", rc));

    RTMemFree(Img.pvData);

    return rc;
#endif
}

int CrFbBltGetContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst)
{
    VBOXVR_LIST List;
    uint32_t c2DRects = 0;
    CR_TEXDATA *pEnteredTex = NULL;
    PCR_BLITTER pEnteredBlitter = NULL;

    VBoxVrListInit(&List);
    int rc = VBoxVrListRectsAdd(&List, 1, CrVrScrCompositorRectGet(&hFb->Compositor), NULL);
    if (!RT_SUCCESS(rc))
    {
        WARN(("VBoxVrListRectsAdd failed rc %d", rc));
        goto end;
    }

    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
    CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter);

    for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter);
            pEntry;
            pEntry = CrVrScrCompositorConstIterNext(&Iter))
    {
        RTPOINT EntryPoint;
        EntryPoint.x = CrVrScrCompositorEntryRectGet(pEntry)->xLeft + pPos->x;
        EntryPoint.y = CrVrScrCompositorEntryRectGet(pEntry)->yTop + pPos->y;

        uint32_t cRegions;
        const RTRECT *pRegions;
        rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
            goto end;
        }

        rc = VBoxVrListRectsSubst(&List, cRegions, pRegions, NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxVrListRectsSubst failed rc %d", rc));
            goto end;
        }

        for (uint32_t j = 0; j < cRegions; ++j)
        {
            /* rects are in dst coordinates,
             * while the pReg is in source coords
             * convert */
            const RTRECT * pReg = &pRegions[j];
            RTRECT SrcReg;
            /* translate */
            VBoxRectTranslated(pReg, pPos->x, pPos->y, &SrcReg);

            for (uint32_t i = 0; i < cRects; ++i)
            {
                const RTRECT * pRect = &pRects[i];

                RTRECT Intersection;
                VBoxRectIntersected(pRect, &SrcReg, &Intersection);
                if (VBoxRectIsZero(&Intersection))
                    continue;

                CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
                const CR_BLITTER_IMG *pSrcImg;

                if (pEnteredTex != pTex)
                {
                    if (!pEnteredBlitter)
                    {
                        pEnteredBlitter = CrTdBlitterGet(pTex);
                        rc = CrBltEnter(pEnteredBlitter);
                        if (!RT_SUCCESS(rc))
                        {
                            WARN(("CrBltEnter failed %d", rc));
                            pEnteredBlitter = NULL;
                            goto end;
                        }
                    }

                    if (pEnteredTex)
                    {
                        CrTdBltLeave(pEnteredTex);

                        pEnteredTex = NULL;

                        if (pEnteredBlitter != CrTdBlitterGet(pTex))
                        {
                            WARN(("blitters not equal!"));
                            CrBltLeave(pEnteredBlitter);

                            pEnteredBlitter = CrTdBlitterGet(pTex);
                            rc = CrBltEnter(pEnteredBlitter);
                             if (!RT_SUCCESS(rc))
                             {
                                 WARN(("CrBltEnter failed %d", rc));
                                 pEnteredBlitter = NULL;
                                 goto end;
                             }
                        }
                    }

                    rc = CrTdBltEnter(pTex);
                    if (!RT_SUCCESS(rc))
                    {
                        WARN(("CrTdBltEnter failed %d", rc));
                        goto end;
                    }

                    pEnteredTex = pTex;
                }

                rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg);
                if (!RT_SUCCESS(rc))
                {
                    WARN(("CrTdBltDataAcquire failed rc %d", rc));
                    goto end;
                }

                bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS);

                CrMBltImgRect(pSrcImg, &EntryPoint, fInvert, &Intersection, pDst);

                CrTdBltDataRelease(pTex);
            }
        }
    }

    c2DRects = VBoxVrListRectsCount(&List);
    if (c2DRects)
    {
        if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT))
        {
            if (g_CrPresenter.pvTmpBuf2)
                RTMemFree(g_CrPresenter.pvTmpBuf2);

            g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT);
            g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2);
            if (!g_CrPresenter.pvTmpBuf2)
            {
                WARN(("RTMemAlloc failed!"));
                g_CrPresenter.cbTmpBuf2 = 0;
                rc = VERR_NO_MEMORY;
                goto end;
            }
        }

        RTRECT *p2DRects  = (RTRECT *)g_CrPresenter.pvTmpBuf2;

        rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects);
        if (!RT_SUCCESS(rc))
        {
            WARN(("VBoxVrListRectsGet failed, rc %d", rc));
            goto end;
        }

        const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor);

        CR_BLITTER_IMG FbImg;

        crFbImgFromFb(hFb, &FbImg);

        CrMBltImg(&FbImg, pPos, c2DRects, p2DRects, pDst);
    }

end:

    if (pEnteredTex)
        CrTdBltLeave(pEnteredTex);

    if (pEnteredBlitter)
        CrBltLeave(pEnteredBlitter);

    VBoxVrListClear(&List);

    return rc;
}

int CrFbBltGetContentsEx(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
{
    uint32_t srcWidth = pSrcRectSize->cx;
    uint32_t srcHeight = pSrcRectSize->cy;
    uint32_t dstWidth = pDstRect->xRight - pDstRect->xLeft;
    uint32_t dstHeight = pDstRect->yBottom - pDstRect->yTop;
    if (srcWidth == dstWidth
            && srcHeight == dstHeight)
    {
        RTPOINT Pos = {pDstRect->xLeft, pDstRect->yTop};
        return CrFbBltGetContents(hFb, &Pos, cRects, pRects, pImg);
    }
    if (!CrFbHas3DData(hFb)
            || (srcWidth * srcHeight > dstWidth * dstHeight))
        return crFbBltGetContentsScaledDirect(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg);

    return crFbBltGetContentsScaledCPU(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg);
}

static void crFbBltPutContentsFbVram(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pSrc)
{
    const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor);

    CR_BLITTER_IMG FbImg;

    crFbImgFromFb(hFb, &FbImg);

    CrMBltImg(pSrc, pPos, cRects, pRects, &FbImg);
}

static void crFbClrFillFbVram(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
{
    CR_BLITTER_IMG FbImg;

    crFbImgFromFb(hFb, &FbImg);

    CrMClrFillImg(&FbImg, cRects, pRects, u32Color);
}

int CrFbClrFill(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
{
    if (!hFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    crFbClrFillFbVram(hFb, cRects, pRects, u32Color);

    RTPOINT DstPoint = {0, 0};

    int rc = CrFbEntryRegionsAdd(hFb, NULL, &DstPoint, cRects, pRects, false);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrFbEntryRegionsAdd failed %d", rc));
        return rc;
    }

    return VINF_SUCCESS;
}

static int crFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
{
    crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg);

    int rc = CrFbEntryRegionsAdd(hFb, NULL, pPos, cRects, pRects, false);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrFbEntryRegionsAdd failed %d", rc));
        return rc;
    }

    return VINF_SUCCESS;
}

int CrFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
{
    if (!hFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    return crFbBltPutContents(hFb, pPos, cRects, pRects, pImg);
}

static int crFbRegionsIsIntersectRects(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, bool *pfRegChanged)
{
    uint32_t cCompRects;
    const RTRECT *pCompRects;
    int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cCompRects, NULL, NULL, &pCompRects);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc));
        return rc;
    }

    bool fRegChanged = false;
    for (uint32_t i = 0; i < cCompRects; ++i)
    {
        const RTRECT *pCompRect = &pCompRects[i];
        for (uint32_t j = 0; j < cRects; ++j)
        {
            const RTRECT *pRect = &pRects[j];
            if (VBoxRectIsIntersect(pCompRect, pRect))
            {
                *pfRegChanged = true;
                return VINF_SUCCESS;
            }
        }
    }

    *pfRegChanged = false;
    return VINF_SUCCESS;
}

int CrFbBltPutContentsNe(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg)
{
    bool fRegChanged = false;
    int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged);
    if (!RT_SUCCESS(rc))
    {
        WARN(("crFbRegionsIsIntersectRects failed rc %d", rc));
        return rc;
    }

    if (fRegChanged)
    {
        rc = CrFbUpdateBegin(hFb);
        if (RT_SUCCESS(rc))
        {
            rc = CrFbBltPutContents(hFb, pPos, cRects, pRects, pImg);
            if (!RT_SUCCESS(rc))
                WARN(("CrFbBltPutContents failed rc %d", rc));
            CrFbUpdateEnd(hFb);
        }
        else
            WARN(("CrFbUpdateBegin failed rc %d", rc));

        return rc;
    }

    crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg);
    return VINF_SUCCESS;
}

int CrFbClrFillNe(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color)
{
    bool fRegChanged = false;
    int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged);
    if (!RT_SUCCESS(rc))
    {
        WARN(("crFbRegionsIsIntersectRects failed rc %d", rc));
        return rc;
    }

    if (fRegChanged)
    {
        rc = CrFbUpdateBegin(hFb);
        if (RT_SUCCESS(rc))
        {
            rc = CrFbClrFill(hFb, cRects, pRects, u32Color);
            if (!RT_SUCCESS(rc))
                WARN(("CrFbClrFill failed rc %d", rc));
            CrFbUpdateEnd(hFb);
        }
        else
            WARN(("CrFbUpdateBegin failed rc %d", rc));

        return rc;
    }

    crFbClrFillFbVram(hFb, cRects, pRects, u32Color);
    return VINF_SUCCESS;
}

int CrFbResize(CR_FRAMEBUFFER *pFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM)
{
    if (!pFb->cUpdating)
    {
        WARN(("no update in progress"));
        return VERR_INVALID_STATE;
    }

    int rc = VINF_SUCCESS;
    if (CrFbIsEnabled(pFb))
    {
        rc = CrFbRegionsClear(pFb);
        if (RT_FAILURE(rc))
        {
            WARN(("CrFbRegionsClear failed %d", rc));
            return rc;
        }
    }

    RTRECT Rect;
    Rect.xLeft = 0;
    Rect.yTop = 0;
    Rect.xRight = pScreen->u32Width;
    Rect.yBottom = pScreen->u32Height;
    rc = CrVrScrCompositorRectSet(&pFb->Compositor, &Rect, NULL);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrVrScrCompositorRectSet failed rc %d", rc));
        return rc;
    }

    pFb->ScreenInfo = *pScreen;
    pFb->pvVram = pvVRAM ? pvVRAM : g_pvVRamBase + pScreen->u32StartOffset;

    if (pFb->pDisplay)
        pFb->pDisplay->FramebufferChanged(pFb);

    return VINF_SUCCESS;
}

void CrFbTerm(CR_FRAMEBUFFER *pFb)
{
    if (pFb->cUpdating)
    {
        WARN(("update in progress"));
        return;
    }
    uint32_t idFb = pFb->ScreenInfo.u32ViewIndex;

    CrVrScrCompositorClear(&pFb->Compositor);
    CrHTableDestroy(&pFb->SlotTable);

    Assert(RTListIsEmpty(&pFb->EntriesList));
    Assert(!pFb->cEntries);

    memset(pFb, 0, sizeof (*pFb));

    pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED;
    pFb->ScreenInfo.u32ViewIndex = idFb;
}

ICrFbDisplay* CrFbDisplayGet(CR_FRAMEBUFFER *pFb)
{
    return pFb->pDisplay;
}

int CrFbDisplaySet(CR_FRAMEBUFFER *pFb, ICrFbDisplay *pDisplay)
{
    if (pFb->cUpdating)
    {
        WARN(("update in progress"));
        return VERR_INVALID_STATE;
    }

    if (pFb->pDisplay == pDisplay)
        return VINF_SUCCESS;

    pFb->pDisplay = pDisplay;

    return VINF_SUCCESS;
}

#define CR_PMGR_MODE_WINDOW 0x1
/* mutually exclusive with CR_PMGR_MODE_WINDOW */
#define CR_PMGR_MODE_ROOTVR 0x2
#define CR_PMGR_MODE_VRDP   0x4
#define CR_PMGR_MODE_ALL    0x7

static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove);
static void crPMgrCleanUnusedDisplays();

static CR_FBTEX* crFbTexAlloc()
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
    return (CR_FBTEX*)RTMemCacheAlloc(g_CrPresenter.FbTexLookasideList);
#else
    return (CR_FBTEX*)RTMemAlloc(sizeof (CR_FBTEX));
#endif
}

static void crFbTexFree(CR_FBTEX *pTex)
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
    RTMemCacheFree(g_CrPresenter.FbTexLookasideList, pTex);
#else
    RTMemFree(pTex);
#endif
}

static CR_FRAMEBUFFER_ENTRY* crFbEntryAlloc()
{
#ifndef VBOXVDBG_MEMCACHE_DISABLE
    return (CR_FRAMEBUFFER_ENTRY*)RTMemCacheAlloc(g_CrPresenter.FbEntryLookasideList);
#else
    return (CR_FRAMEBUFFER_ENTRY*)RTMemAlloc(sizeof (CR_FRAMEBUFFER_ENTRY));
#endif
}

static void crFbEntryFree(CR_FRAMEBUFFER_ENTRY *pEntry)
{
    Assert(!CrVrScrCompositorEntryIsUsed(&pEntry->Entry));
#ifndef VBOXVDBG_MEMCACHE_DISABLE
    RTMemCacheFree(g_CrPresenter.FbEntryLookasideList, pEntry);
#else
    RTMemFree(pEntry);
#endif
}

DECLCALLBACK(void) crFbTexRelease(CR_TEXDATA *pTex)
{
    CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTex);
    CRTextureObj *pTobj = pFbTex->pTobj;

    CrTdBltDataCleanupNe(pTex);

    if (pTobj)
    {
        crHashtableDelete(g_CrPresenter.pFbTexMap, pTobj->id, NULL);

        crStateReleaseTexture(cr_server.MainContextInfo.pContext, pTobj);


        crStateGlobalSharedRelease();
    }

    crFbTexFree(pFbTex);
}

void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased)
{
    PCR_BLITTER pBlitter = crServerVBoxBlitterGet();

    CrTdInit(pFbTex, pTex, pBlitter, pfnTextureReleased);
}

static CR_FBTEX* crFbTexCreate(const VBOXVR_TEXTURE *pTex)
{
    CR_FBTEX *pFbTex = crFbTexAlloc();
    if (!pFbTex)
    {
        WARN(("crFbTexAlloc failed!"));
        return NULL;
    }

    CrFbTexDataInit(&pFbTex->Tex, pTex, crFbTexRelease);
    pFbTex->pTobj = NULL;

    return pFbTex;
}

CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex)
{
    CR_FBTEX *pFbTex = crFbTexCreate(pTex);
    if (!pFbTex)
    {
        WARN(("crFbTexCreate failed!"));
        return NULL;
    }

    return &pFbTex->Tex;
}

static CR_FBTEX* crFbTexAcquire(GLuint idTexture)
{
    CR_FBTEX *pFbTex = (CR_FBTEX *)crHashtableSearch(g_CrPresenter.pFbTexMap, idTexture);
    if (pFbTex)
    {
        CrTdAddRef(&pFbTex->Tex);
        return pFbTex;
    }

    CRSharedState *pShared = crStateGlobalSharedAcquire();
    if (!pShared)
    {
        WARN(("pShared is null!"));
        return NULL;
    }

    CRTextureObj *pTobj = (CRTextureObj*)crHashtableSearch(pShared->textureTable, idTexture);
    if (!pTobj)
    {
        LOG(("pTobj is null!"));
        crStateGlobalSharedRelease();
        return NULL;
    }

    Assert(pTobj->id == idTexture);

    GLuint hwid = crStateGetTextureObjHWID(pTobj);
    if (!hwid)
    {
        WARN(("hwId is null!"));
        crStateGlobalSharedRelease();
        return NULL;
    }

    VBOXVR_TEXTURE Tex;
    Tex.width = pTobj->level[0]->width;
    Tex.height = pTobj->level[0]->height;
    Tex.hwid = hwid;
    Tex.target = pTobj->target;

    pFbTex = crFbTexCreate(&Tex);
    if (!pFbTex)
    {
        WARN(("crFbTexCreate failed!"));
        crStateGlobalSharedRelease();
        return NULL;
    }

    CR_STATE_SHAREDOBJ_USAGE_SET(pTobj, cr_server.MainContextInfo.pContext);

    pFbTex->pTobj = pTobj;

    crHashtableAdd(g_CrPresenter.pFbTexMap, idTexture, pFbTex);

    return pFbTex;
}

static CR_TEXDATA* CrFbTexDataAcquire(GLuint idTexture)
{
    CR_FBTEX* pTex = crFbTexAcquire(idTexture);
    if (!pTex)
    {
        WARN(("crFbTexAcquire failed for %d", idTexture));
        return NULL;
    }

    return &pTex->Tex;
}

static void crFbEntryMarkDestroyed(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
{
    if (pEntry->Flags.fCreateNotified)
    {
        pEntry->Flags.fCreateNotified = 0;
        if (pFb->pDisplay)
            pFb->pDisplay->EntryDestroyed(pFb, pEntry);

        CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry);
        if (pTex)
            CrTdBltDataInvalidateNe(pTex);
    }
}

static void crFbEntryDestroy(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
{
    crFbEntryMarkDestroyed(pFb, pEntry);
    CrVrScrCompositorEntryCleanup(&pEntry->Entry);
    CrHTableDestroy(&pEntry->HTable);
    Assert(pFb->cEntries);
    RTListNodeRemove(&pEntry->Node);
    --pFb->cEntries;
    crFbEntryFree(pEntry);
}

DECLINLINE(uint32_t) crFbEntryAddRef(CR_FRAMEBUFFER_ENTRY* pEntry)
{
    return ++pEntry->cRefs;
}

DECLINLINE(uint32_t) crFbEntryRelease(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry)
{
    uint32_t cRefs = --pEntry->cRefs;
    if (!cRefs)
        crFbEntryDestroy(pFb, pEntry);
    return cRefs;
}

static DECLCALLBACK(void) crFbEntryReleased(const struct VBOXVR_SCR_COMPOSITOR *pCompositor, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacingEntry)
{
    CR_FRAMEBUFFER *pFb = PCR_FRAMEBUFFER_FROM_COMPOSITOR(pCompositor);
    CR_FRAMEBUFFER_ENTRY *pFbEntry = PCR_FBENTRY_FROM_ENTRY(pEntry);
    CR_FRAMEBUFFER_ENTRY *pFbReplacingEntry = pReplacingEntry ? PCR_FBENTRY_FROM_ENTRY(pReplacingEntry) : NULL;
    if (pFbReplacingEntry)
    {
        /*replace operation implies the replaced entry gets auto-destroyed,
         * while all its data gets moved to the *clean* replacing entry
         * 1. ensure the replacing entry is cleaned up */
        crFbEntryMarkDestroyed(pFb, pFbReplacingEntry);

        CrHTableMoveTo(&pFbEntry->HTable, &pFbReplacingEntry->HTable);

        CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry);
        CR_TEXDATA *pReplacingTex = CrVrScrCompositorEntryTexGet(&pFbReplacingEntry->Entry);

        CrTdBltScaleCacheMoveTo(pTex, pReplacingTex);

        if (pFb->pDisplay)
            pFb->pDisplay->EntryReplaced(pFb, pFbReplacingEntry, pFbEntry);

        CrTdBltDataInvalidateNe(pTex);

        /* 2. mark the replaced entry is destroyed */
        Assert(pFbEntry->Flags.fCreateNotified);
        Assert(pFbEntry->Flags.fInList);
        pFbEntry->Flags.fCreateNotified = 0;
        pFbEntry->Flags.fInList = 0;
        pFbReplacingEntry->Flags.fCreateNotified = 1;
        pFbReplacingEntry->Flags.fInList = 1;
    }
    else
    {
        if (pFbEntry->Flags.fInList)
        {
            pFbEntry->Flags.fInList = 0;
            if (pFb->pDisplay)
                pFb->pDisplay->EntryRemoved(pFb, pFbEntry);

            CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry);
            if (pTex)
                CrTdBltDataInvalidateNe(pTex);
        }
    }

    crFbEntryRelease(pFb, pFbEntry);
}

static CR_FRAMEBUFFER_ENTRY* crFbEntryCreate(CR_FRAMEBUFFER *pFb, CR_TEXDATA* pTex, const RTRECT *pRect, uint32_t fFlags)
{
    CR_FRAMEBUFFER_ENTRY *pEntry = crFbEntryAlloc();
    if (!pEntry)
    {
        WARN(("crFbEntryAlloc failed!"));
        return NULL;
    }

    CrVrScrCompositorEntryInit(&pEntry->Entry, pRect, pTex, crFbEntryReleased);
    CrVrScrCompositorEntryFlagsSet(&pEntry->Entry, fFlags);
    pEntry->cRefs = 1;
    pEntry->Flags.Value = 0;
    CrHTableCreate(&pEntry->HTable, 0);

    RTListAppend(&pFb->EntriesList, &pEntry->Node);
    ++pFb->cEntries;

    return pEntry;
}

int CrFbEntryCreateForTexData(CR_FRAMEBUFFER *pFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry)
{
    if (pTex == NULL)
    {
        WARN(("pTex is NULL"));
        return VERR_INVALID_PARAMETER;
    }

    RTRECT Rect;
    Rect.xLeft = 0;
    Rect.yTop = 0;
    Rect.xRight = pTex->Tex.width;
    Rect.yBottom = pTex->Tex.height;
    CR_FRAMEBUFFER_ENTRY* pEntry = crFbEntryCreate(pFb, pTex, &Rect, fFlags);
    if (!pEntry)
    {
        WARN(("crFbEntryCreate failed"));
        return VERR_NO_MEMORY;
    }

    *phEntry = pEntry;
    return VINF_SUCCESS;
}

int CrFbEntryTexDataUpdate(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex)
{
    if (!pFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    if (pTex)
        CrVrScrCompositorEntryTexSet(&pEntry->Entry, pTex);

    if (CrVrScrCompositorEntryIsUsed(&pEntry->Entry))
    {
        if (pFb->pDisplay)
            pFb->pDisplay->EntryTexChanged(pFb, pEntry);

        CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry);
        if (pTex)
            CrTdBltDataInvalidateNe(pTex);
    }

    return VINF_SUCCESS;
}


int CrFbEntryCreateForTexId(CR_FRAMEBUFFER *pFb, GLuint idTexture, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry)
{
    CR_FBTEX* pFbTex = crFbTexAcquire(idTexture);
    if (!pFbTex)
    {
        LOG(("crFbTexAcquire failed"));
        return VERR_INVALID_PARAMETER;
    }

    CR_TEXDATA* pTex = &pFbTex->Tex;
    int rc = CrFbEntryCreateForTexData(pFb, pTex, fFlags, phEntry);
    if (!RT_SUCCESS(rc))
    {
    	WARN(("CrFbEntryCreateForTexData failed rc %d", rc));
    }

    /*always release the tex, the CrFbEntryCreateForTexData will do incref as necessary */
    CrTdRelease(pTex);
    return rc;
}

void CrFbEntryAddRef(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    ++hEntry->cRefs;
}

void CrFbEntryRelease(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    crFbEntryRelease(pFb, hEntry);
}

static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary);

int CrFbRegionsClear(HCR_FRAMEBUFFER hFb)
{
    if (!hFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    uint32_t cRegions;
    const RTRECT *pRegions;
    int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cRegions, NULL, NULL, &pRegions);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc));
        return rc;
    }

    const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(hFb);
    VBOXCMDVBVAOFFSET offVRAM = (VBOXCMDVBVAOFFSET)(((uintptr_t)CrFbGetVRAM(hFb)) - ((uintptr_t)g_pvVRamBase));
    RTPOINT Pos = {0,0};
    int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(pScreen->u32ViewIndex, offVRAM, pScreen->u32Width, pScreen->u32Height, &Pos, cRegions, pRegions, true);
    if (i8Result)
    {
        WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed"));
        return VERR_INTERNAL_ERROR;
    }

#ifdef DEBUG
    {
        uint32_t cTmpRegions;
        const RTRECT *pTmpRegions;
        int tmpRc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cTmpRegions, NULL, NULL, &pTmpRegions);
        if (!RT_SUCCESS(tmpRc))
        {
            WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", tmpRc));
        }
        Assert(!cTmpRegions);
    }
#endif

    /* just in case */
    bool fChanged = false;
    CrVrScrCompositorRegionsClear(&hFb->Compositor, &fChanged);
    Assert(!fChanged);

    if (cRegions)
    {
        if (hFb->pDisplay)
            hFb->pDisplay->RegionsChanged(hFb);
    }

    return VINF_SUCCESS;
}

int CrFbEntryRegionsAdd(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated)
{
    if (!pFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    uint32_t fChangeFlags = 0;
    VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL;
    VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry;
    bool fEntryWasInList;

    if (hEntry)
    {
        crFbEntryAddRef(hEntry);
        pNewEntry = &hEntry->Entry;
        fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry);

        Assert(!hEntry->Flags.fInList == !fEntryWasInList);
    }
    else
    {
        pNewEntry = NULL;
        fEntryWasInList = false;
    }

    int rc = CrVrScrCompositorEntryRegionsAdd(&pFb->Compositor, hEntry ? &hEntry->Entry : NULL, pPos, cRegions, paRegions, fPosRelated, &pReplacedScrEntry, &fChangeFlags);
    if (RT_SUCCESS(rc))
    {
        if (fChangeFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED)
        {
            if (!fEntryWasInList && pNewEntry)
            {
                Assert(CrVrScrCompositorEntryIsUsed(pNewEntry));
                if (!hEntry->Flags.fCreateNotified)
                {
                    hEntry->Flags.fCreateNotified = 1;
                    if (pFb->pDisplay)
                        pFb->pDisplay->EntryCreated(pFb, hEntry);
                }

#ifdef DEBUG_misha
                /* in theory hEntry->Flags.fInList can be set if entry is replaced,
                 * but then modified to fit the compositor rects,
                 * and so we get the regions changed notification as a result
                 * this should not generally happen though, so put an assertion to debug that situation */
                Assert(!hEntry->Flags.fInList);
#endif
                if (!hEntry->Flags.fInList)
                {
                    hEntry->Flags.fInList = 1;

                    if (pFb->pDisplay)
                        pFb->pDisplay->EntryAdded(pFb, hEntry);
                }
            }
            if (pFb->pDisplay)
                pFb->pDisplay->RegionsChanged(pFb);

            Assert(!pReplacedScrEntry);
        }
        else if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED)
        {
            Assert(pReplacedScrEntry);
            /* we have already processed that in a "release" callback */
            Assert(hEntry);
        }
        else
        {
            Assert(!fChangeFlags);
            Assert(!pReplacedScrEntry);
        }

        if (hEntry)
        {
            if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry))
            {
                if (pFb->pDisplay)
                    pFb->pDisplay->EntryTexChanged(pFb, hEntry);

                CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry);
                if (pTex)
                    CrTdBltDataInvalidateNe(pTex);
            }
        }
    }
    else
        WARN(("CrVrScrCompositorEntryRegionsAdd failed, rc %d", rc));

    return rc;
}

int CrFbEntryRegionsSet(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated)
{
    if (!pFb->cUpdating)
    {
        WARN(("framebuffer not updating"));
        return VERR_INVALID_STATE;
    }

    bool fChanged = 0;
    VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL;
    VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry;
    bool fEntryWasInList;

    if (hEntry)
    {
        crFbEntryAddRef(hEntry);
        pNewEntry = &hEntry->Entry;
        fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry);
        Assert(!hEntry->Flags.fInList == !fEntryWasInList);
    }
    else
    {
        pNewEntry = NULL;
        fEntryWasInList = false;
    }

    int rc = CrVrScrCompositorEntryRegionsSet(&pFb->Compositor, pNewEntry, pPos, cRegions, paRegions, fPosRelated, &fChanged);
    if (RT_SUCCESS(rc))
    {
        if (fChanged)
        {
            if (!fEntryWasInList && pNewEntry)
            {
                if (CrVrScrCompositorEntryIsUsed(pNewEntry))
                {
                    if (!hEntry->Flags.fCreateNotified)
                    {
                        hEntry->Flags.fCreateNotified = 1;

                        if (pFb->pDisplay)
                            pFb->pDisplay->EntryCreated(pFb, hEntry);
                    }

                    Assert(!hEntry->Flags.fInList);
                    hEntry->Flags.fInList = 1;

                    if (pFb->pDisplay)
                        pFb->pDisplay->EntryAdded(pFb, hEntry);
                }
            }

            if (pFb->pDisplay)
                pFb->pDisplay->RegionsChanged(pFb);
        }

        if (hEntry)
        {
            if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry))
            {
                if (pFb->pDisplay)
                    pFb->pDisplay->EntryTexChanged(pFb, hEntry);

                CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry);
                if (pTex)
                    CrTdBltDataInvalidateNe(pTex);
            }
        }
    }
    else
        WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc));

    return rc;
}

const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry)
{
    return &hEntry->Entry;
}

HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry)
{
    return RT_FROM_MEMBER(pCEntry, CR_FRAMEBUFFER_ENTRY, Entry);
}

void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext)
{
    HCR_FRAMEBUFFER_ENTRY hEntry, hNext;
    RTListForEachSafe(&hFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node)
    {
        if (hEntry->Flags.fCreateNotified)
        {
            if (!pfnVisitorCb(hFb, hEntry, pvContext))
                return;
        }
    }
}


CRHTABLE_HANDLE CrFbDDataAllocSlot(CR_FRAMEBUFFER *pFb)
{
    return CrHTablePut(&pFb->SlotTable, (void*)1);
}

void CrFbDDataReleaseSlot(CR_FRAMEBUFFER *pFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext)
{
    HCR_FRAMEBUFFER_ENTRY hEntry, hNext;
    RTListForEachSafe(&pFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node)
    {
        if (CrFbDDataEntryGet(hEntry, hSlot))
        {
            if (pfnReleaseCb)
                pfnReleaseCb(pFb, hEntry, pvContext);

            CrFbDDataEntryClear(hEntry, hSlot);
        }
    }

    CrHTableRemove(&pFb->SlotTable, hSlot);
}

int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData)
{
    return CrHTablePutToSlot(&hEntry->HTable, hSlot, pvData);
}

void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot)
{
    return CrHTableRemove(&hEntry->HTable, hSlot);
}

void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot)
{
    return CrHTableGet(&hEntry->HTable, hSlot);
}

typedef union CR_FBDISPBASE_FLAGS
{
    struct {
        uint32_t fRegionsShanged : 1;
        uint32_t Reserved        : 31;
    };
    uint32_t u32Value;
} CR_FBDISPBASE_FLAGS;

class CrFbDisplayBase : public ICrFbDisplay
{
public:
    CrFbDisplayBase() :
        mpContainer(NULL),
        mpFb(NULL),
        mcUpdates(0),
        mhSlot(CRHTABLE_HANDLE_INVALID)
    {
        mFlags.u32Value = 0;
    }

    virtual bool isComposite()
    {
        return false;
    }

    class CrFbDisplayComposite* getContainer()
    {
        return mpContainer;
    }

    bool isInList()
    {
        return !!mpContainer;
    }

    bool isUpdating()
    {
        return !!mcUpdates;
    }

    int setRegionsChanged()
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        mFlags.fRegionsShanged = 1;
        return VINF_SUCCESS;
    }

    int setFramebuffer(struct CR_FRAMEBUFFER *pFb)
    {
        if (mcUpdates)
        {
            WARN(("trying to set framebuffer while update is in progress"));
            return VERR_INVALID_STATE;
        }

        if (mpFb == pFb)
            return VINF_SUCCESS;

        int rc = setFramebufferBegin(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        if (mpFb)
        {
            rc = fbCleanup();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                setFramebufferEnd(pFb);
                return rc;
            }
        }

        mpFb = pFb;

        if (mpFb)
        {
            rc = fbSync();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                setFramebufferEnd(pFb);
                return rc;
            }
        }

        setFramebufferEnd(pFb);
        return VINF_SUCCESS;
    }

    struct CR_FRAMEBUFFER* getFramebuffer()
    {
        return mpFb;
    }

    virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb)
    {
        ++mcUpdates;
        Assert(!mFlags.fRegionsShanged || mcUpdates > 1);
        return VINF_SUCCESS;
    }

    virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb)
    {
        --mcUpdates;
        Assert(mcUpdates < UINT32_MAX/2);
        if (!mcUpdates)
            onUpdateEnd();
    }

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        return VINF_SUCCESS;
    }

    virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        mFlags.fRegionsShanged = 1;
        return VINF_SUCCESS;
    }

    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        return VINF_SUCCESS;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        return VINF_SUCCESS;
    }

    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        mFlags.fRegionsShanged = 1;
        return VINF_SUCCESS;
    }

    virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        return VINF_SUCCESS;
    }

    virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        mFlags.fRegionsShanged = 1;
        return VINF_SUCCESS;
    }

    virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        mFlags.fRegionsShanged = 1;
        return VINF_SUCCESS;
    }

    virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
    {
        if (!mcUpdates)
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }
        return VINF_SUCCESS;
    }

    virtual ~CrFbDisplayBase();

    /*@todo: move to protected and switch from RTLISTNODE*/
    RTLISTNODE mNode;
    class CrFbDisplayComposite* mpContainer;
protected:
    virtual void onUpdateEnd()
    {
        if (mFlags.fRegionsShanged)
        {
            mFlags.fRegionsShanged = 0;
            if (getFramebuffer()) /*<-dont't do anything on cleanup*/
                ueRegions();
        }
    }

    virtual void ueRegions()
    {
    }

    static DECLCALLBACK(bool) entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
    {
        int rc = ((ICrFbDisplay*)(pvContext))->EntryCreated(hFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
        }
        return true;
    }

    static DECLCALLBACK(bool) entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
    {
        int rc = ((ICrFbDisplay*)(pvContext))->EntryDestroyed(hFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
        }
        return true;
    }

    int fbSynchAddAllEntries()
    {
        VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
        const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;

        CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter);

        int rc = VINF_SUCCESS;

        CrFbVisitCreatedEntries(mpFb, entriesCreateCb, this);

        while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
        {
            HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);

            rc = EntryAdded(mpFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                EntryDestroyed(mpFb, hEntry);
                break;
            }
        }

        return rc;
    }

    int fbCleanupRemoveAllEntries()
    {
        VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
        const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;

        CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter);

        int rc = VINF_SUCCESS;

        while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
        {
            HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
            rc = EntryRemoved(mpFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                break;
            }

            CrFbVisitCreatedEntries(mpFb, entriesDestroyCb, this);
        }

        return rc;
    }

    virtual int setFramebufferBegin(struct CR_FRAMEBUFFER *pFb)
    {
        return UpdateBegin(pFb);
    }
    virtual void setFramebufferEnd(struct CR_FRAMEBUFFER *pFb)
    {
        UpdateEnd(pFb);
    }

    static DECLCALLBACK(void) slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext)
    {
    }

    virtual void slotRelease()
    {
        Assert(mhSlot);
        CrFbDDataReleaseSlot(mpFb, mhSlot, slotEntryReleaseCB, this);
    }

    virtual int fbCleanup()
    {
        if (mhSlot)
        {
            slotRelease();
            mhSlot = 0;
        }
        mpFb = NULL;
        return VINF_SUCCESS;
    }

    virtual int fbSync()
    {
        return VINF_SUCCESS;
    }

    CRHTABLE_HANDLE slotGet()
    {
        if (!mhSlot)
        {
            if (mpFb)
                mhSlot = CrFbDDataAllocSlot(mpFb);
        }

        return mhSlot;
    }

private:
    struct CR_FRAMEBUFFER *mpFb;
    uint32_t mcUpdates;
    CRHTABLE_HANDLE mhSlot;
    CR_FBDISPBASE_FLAGS mFlags;
};

class CrFbDisplayComposite : public CrFbDisplayBase
{
public:
    CrFbDisplayComposite() :
        mcDisplays(0)
    {
        RTListInit(&mDisplays);
    }

    virtual bool isComposite()
    {
        return true;
    }

    uint32_t getDisplayCount()
    {
        return mcDisplays;
    }

    bool add(CrFbDisplayBase *pDisplay)
    {
        if (pDisplay->isInList())
        {
            WARN(("entry in list already"));
            return false;
        }

        RTListAppend(&mDisplays, &pDisplay->mNode);
        pDisplay->mpContainer = this;
        pDisplay->setFramebuffer(getFramebuffer());
        ++mcDisplays;
        return true;
    }

    bool remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay = true)
    {
        if (pDisplay->getContainer() != this)
        {
            WARN(("invalid entry container"));
            return false;
        }

        RTListNodeRemove(&pDisplay->mNode);
        pDisplay->mpContainer = NULL;
        if (fCleanupDisplay)
            pDisplay->setFramebuffer(NULL);
        --mcDisplays;
        return true;
    }

    CrFbDisplayBase* first()
    {
        return RTListGetFirstCpp(&mDisplays, CrFbDisplayBase, mNode);
    }

    CrFbDisplayBase* next(CrFbDisplayBase* pDisplay)
    {
        if (pDisplay->getContainer() != this)
        {
            WARN(("invalid entry container"));
            return NULL;
        }

        return RTListGetNextCpp(&mDisplays, pDisplay, CrFbDisplayBase, mNode);
    }

    virtual int setFramebuffer(struct CR_FRAMEBUFFER *pFb)
    {
        CrFbDisplayBase::setFramebuffer(pFb);

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            pIter->setFramebuffer(pFb);
        }

        return VINF_SUCCESS;
    }

    virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::UpdateBegin(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            rc = pIter->UpdateBegin(pFb);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb)
    {
        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            pIter->UpdateEnd(pFb);
        }

        CrFbDisplayBase::UpdateEnd(pFb);
    }

    virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryAdded(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryCreated(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryReplaced(pFb, hNewEntry, hReplacedEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryTexChanged(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryRemoved(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->EntryDestroyed(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::RegionsChanged(pFb);
          if (!RT_SUCCESS(rc))
          {
              WARN(("err"));
              return rc;
          }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->RegionsChanged(pFb);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::FramebufferChanged(pFb);
          if (!RT_SUCCESS(rc))
          {
              WARN(("err"));
              return rc;
          }

        CrFbDisplayBase *pIter;
        RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode)
        {
            int rc = pIter->FramebufferChanged(pFb);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        return VINF_SUCCESS;
    }

    virtual ~CrFbDisplayComposite()
    {
        cleanup();
    }

    void cleanup(bool fCleanupDisplays = true)
    {
        CrFbDisplayBase *pIter, *pIterNext;
        RTListForEachSafeCpp(&mDisplays, pIter, pIterNext, CrFbDisplayBase, mNode)
        {
            remove(pIter, fCleanupDisplays);
        }
    }
private:
    RTLISTNODE mDisplays;
    uint32_t mcDisplays;
};

typedef union CR_FBWIN_FLAGS
{
    struct {
        uint32_t fVisible : 1;
        uint32_t fDataPresented : 1;
        uint32_t fForcePresentOnReenable : 1;
        uint32_t fCompositoEntriesModified : 1;
        uint32_t Reserved : 28;
    };
    uint32_t Value;
} CR_FBWIN_FLAGS;

class CrFbWindow
{
public:
    CrFbWindow(uint64_t parentId) :
        mSpuWindow(0),
        mpCompositor(NULL),
        mcUpdates(0),
        mxPos(0),
        myPos(0),
        mWidth(0),
        mHeight(0),
        mParentId(parentId)
    {
        mFlags.Value = 0;
    }

    bool IsCreated() const
    {
        return !!mSpuWindow;
    }

    bool IsVisivle() const
    {
        return mFlags.fVisible;
    }

    void Destroy()
    {
        CRASSERT(!mcUpdates);

        if (!mSpuWindow)
            return;

        cr_server.head_spu->dispatch_table.WindowDestroy(mSpuWindow);

        mSpuWindow = 0;
        mFlags.fDataPresented = 0;
    }

    int Reparent(uint64_t parentId)
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        uint64_t oldParentId = mParentId;

        mParentId = parentId;

        if (mSpuWindow)
        {
            if (oldParentId && !parentId && mFlags.fVisible)
                cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, false);

            renderspuSetWindowId(mParentId);
            renderspuReparentWindow(mSpuWindow);
            renderspuSetWindowId(cr_server.screen[0].winID);

            if (parentId)
            {
                if (mFlags.fVisible)
                    cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);
                cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, mFlags.fVisible);
            }
        }

        return VINF_SUCCESS;
    }

    int SetVisible(bool fVisible)
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        LOG(("CrWIN: Vidible [%d]", fVisible));

        if (!fVisible != !mFlags.fVisible)
        {
            mFlags.fVisible = fVisible;
            if (mSpuWindow && mParentId)
            {
                if (fVisible)
                    cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);
                cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, fVisible);
            }
        }

        return VINF_SUCCESS;
    }

    int SetSize(uint32_t width, uint32_t height)
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        LOG(("CrWIN: Size [%d ; %d]", width, height));

        if (mWidth != width || mHeight != height)
        {
            mFlags.fCompositoEntriesModified = 1;
            mWidth = width;
            mHeight = height;
            if (mSpuWindow)
                cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, width, height);
        }

        return VINF_SUCCESS;
    }

    int SetPosition(int32_t x, int32_t y)
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        LOG(("CrWIN: Pos [%d ; %d]", x, y));
//      always do WindowPosition to ensure window is adjusted properly
//        if (x != mxPos || y != myPos)
        {
            mxPos = x;
            myPos = y;
            if (mSpuWindow)
                cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, x, y);
        }

        return VINF_SUCCESS;
    }

    int SetVisibleRegionsChanged()
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        mFlags.fCompositoEntriesModified = 1;
        return VINF_SUCCESS;
    }

    int SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor)
    {
        if (!checkInitedUpdating())
        {
            WARN(("err"));
            return VERR_INVALID_STATE;
        }

        mpCompositor = pCompositor;
        mFlags.fCompositoEntriesModified = 1;
        return VINF_SUCCESS;
    }

    int UpdateBegin()
    {
        ++mcUpdates;
        if (mcUpdates > 1)
            return VINF_SUCCESS;

        Assert(!mFlags.fForcePresentOnReenable);
//        Assert(!mFlags.fCompositoEntriesModified);

        if (mFlags.fDataPresented)
        {
            Assert(mSpuWindow);
            cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, NULL, NULL);
            mFlags.fForcePresentOnReenable = isPresentNeeded();
        }

        return VINF_SUCCESS;
    }

    void UpdateEnd()
    {
        --mcUpdates;
        Assert(mcUpdates < UINT32_MAX/2);
        if (mcUpdates)
            return;

        checkRegions();

        if (mSpuWindow)
        {
            bool fPresentNeeded = isPresentNeeded();
            if (fPresentNeeded || mFlags.fForcePresentOnReenable)
            {
                mFlags.fForcePresentOnReenable = false;
                if (mpCompositor)
                    cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, mpCompositor, NULL);
                else
                {
                    VBOXVR_SCR_COMPOSITOR TmpCompositor;
                    RTRECT Rect;
                    Rect.xLeft = 0;
                    Rect.yTop = 0;
                    Rect.xRight = mWidth;
                    Rect.yBottom = mHeight;
                    CrVrScrCompositorInit(&TmpCompositor, &Rect);
                    /* this is a cleanup operation
                     * empty compositor is guarantid to be released on VBoxPresentComposition return */
                    cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, &TmpCompositor, NULL);
                }
                g_pLed->Asserted.s.fWriting = 1;
            }

            /* even if the above branch is entered due to mFlags.fForcePresentOnReenable,
             * the backend should clean up the compositor as soon as presentation is performed */
            mFlags.fDataPresented = fPresentNeeded;
        }
        else
        {
            Assert(!mFlags.fDataPresented);
            Assert(!mFlags.fForcePresentOnReenable);
        }
    }

    uint64_t GetParentId()
    {
        return mParentId;
    }

    int Create()
    {
        if (mSpuWindow)
        {
            //WARN(("window already created"));
            return VINF_ALREADY_INITIALIZED;
        }

        CRASSERT(cr_server.fVisualBitsDefault);
        renderspuSetWindowId(mParentId);
        mSpuWindow = cr_server.head_spu->dispatch_table.WindowCreate("", cr_server.fVisualBitsDefault);
        renderspuSetWindowId(cr_server.screen[0].winID);
        if (mSpuWindow < 0) {
            WARN(("WindowCreate failed"));
            return VERR_GENERAL_FAILURE;
        }

        cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, mWidth, mHeight);
        cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos);

        checkRegions();

        if (mParentId && mFlags.fVisible)
            cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, true);

        return VINF_SUCCESS;
    }

    ~CrFbWindow()
    {
        Destroy();
    }
protected:
    void checkRegions()
    {
        if (!mSpuWindow)
            return;

        if (!mFlags.fCompositoEntriesModified)
            return;

        uint32_t cRects;
        const RTRECT *pRects;
        if (mpCompositor)
        {
            int rc = CrVrScrCompositorRegionsGet(mpCompositor, &cRects, NULL, &pRects, NULL);
            if (!RT_SUCCESS(rc))
            {
                WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc));
                cRects = 0;
                pRects = NULL;
            }
        }
        else
        {
            cRects = 0;
            pRects = NULL;
        }

        cr_server.head_spu->dispatch_table.WindowVisibleRegion(mSpuWindow, cRects, (const GLint*)pRects);

        mFlags.fCompositoEntriesModified = 0;
    }

    bool isPresentNeeded()
    {
        return mFlags.fVisible && mWidth && mHeight && mpCompositor && !CrVrScrCompositorIsEmpty(mpCompositor);
    }

    bool checkInitedUpdating()
    {
        if (!mcUpdates)
        {
            WARN(("not updating"));
            return false;
        }

        return true;
    }
private:
    GLint mSpuWindow;
    const struct VBOXVR_SCR_COMPOSITOR * mpCompositor;
    uint32_t mcUpdates;
    int32_t mxPos;
    int32_t myPos;
    uint32_t mWidth;
    uint32_t mHeight;
    CR_FBWIN_FLAGS mFlags;
    uint64_t mParentId;
};

typedef union CR_FBDISPWINDOW_FLAGS
{
    struct {
        uint32_t fNeVisible : 1;
        uint32_t fNeForce   : 1;
        uint32_t Reserved   : 30;
    };
    uint32_t u32Value;
} CR_FBDISPWINDOW_FLAGS;
class CrFbDisplayWindow : public CrFbDisplayBase
{
public:
    CrFbDisplayWindow(const RTRECT *pViewportRect, uint64_t parentId, uint64_t defaultParentId) :
        mpWindow(NULL),
        mViewportRect(*pViewportRect),
        mu32Screen(~0),
        mParentId(parentId),
        mDefaultParentId(defaultParentId)
    {
        mFlags.u32Value = 0;
    }

    virtual ~CrFbDisplayWindow()
    {
        if (mpWindow)
            delete mpWindow;
    }

    virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = mpWindow ? mpWindow->UpdateBegin() : VINF_SUCCESS;
        if (RT_SUCCESS(rc))
        {
            rc = CrFbDisplayBase::UpdateBegin(pFb);
            if (RT_SUCCESS(rc))
                return VINF_SUCCESS;
            else
            {
                WARN(("err"));
                if (mpWindow)
                    mpWindow->UpdateEnd();
            }
        }
        else
            WARN(("err"));

        return rc;
    }

    virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb)
    {
        CrFbDisplayBase::UpdateEnd(pFb);

        if (mpWindow)
            mpWindow->UpdateEnd();
    }

    virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::RegionsChanged(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        if (mpWindow && mpWindow->GetParentId())
        {
            rc = mpWindow->Create();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        if (mpWindow && mpWindow->GetParentId())
        {
            rc = mpWindow->Create();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        if (mpWindow && mpWindow->GetParentId())
        {
            rc = mpWindow->Create();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        if (mpWindow && mpWindow->GetParentId())
        {
            rc = mpWindow->Create();
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::FramebufferChanged(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return screenChanged();
    }

    const RTRECT* getViewportRect()
    {
        return &mViewportRect;
    }

    virtual int setViewportRect(const RTRECT *pViewportRect)
    {
        if (!isUpdating())
        {
            WARN(("not updating!"));
            return VERR_INVALID_STATE;
        }

// always call SetPosition to ensure window is adjustep properly
//        if (pViewportRect->xLeft != mViewportRect.xLeft || pViewportRect->yTop != mViewportRect.yTop)
        if (mpWindow)
        {
            const RTRECT* pRect = getRect();
            int rc = mpWindow->SetPosition(pRect->xLeft - pViewportRect->xLeft, pRect->yTop - pViewportRect->yTop);
            if (!RT_SUCCESS(rc))
            {
                WARN(("SetPosition failed"));
                return rc;
            }
        }

        mViewportRect = *pViewportRect;

        return VINF_SUCCESS;
    }

    virtual CrFbWindow * windowDetach(bool fCleanup = true)
    {
        if (isUpdating())
        {
            WARN(("updating!"));
            return NULL;
        }

        CrFbWindow * pWindow = mpWindow;
        if (mpWindow)
        {
            if (fCleanup)
                windowCleanup();
            mpWindow = NULL;
        }
        return pWindow;
    }

    virtual CrFbWindow * windowAttach(CrFbWindow * pNewWindow)
    {
        if (isUpdating())
        {
            WARN(("updating!"));
            return NULL;
        }

        CrFbWindow * pOld = mpWindow;
        if (mpWindow)
            windowDetach();

        mpWindow = pNewWindow;
        if (pNewWindow)
            windowSync();

        return mpWindow;
    }

    virtual int setDefaultParent(uint64_t parentId)
    {
        mDefaultParentId = parentId;

        if (!isActive() && mpWindow)
        {
            int rc = mpWindow->Reparent(parentId);
            if (!RT_SUCCESS(rc))
            {
                WARN(("window reparent failed"));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    virtual int reparent(uint64_t parentId)
    {
        if (!isUpdating())
        {
            WARN(("not updating!"));
            return VERR_INVALID_STATE;
        }

        mParentId = parentId;
        int rc = VINF_SUCCESS;

        if (isActive() && mpWindow)
        {
            rc = mpWindow->Reparent(parentId);
            if (!RT_SUCCESS(rc))
                WARN(("window reparent failed"));

            mFlags.fNeForce = 1;
        }

        return rc;
    }

    virtual bool isVisible()
    {
        HCR_FRAMEBUFFER hFb = getFramebuffer();
        if (!hFb)
            return false;
        const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb);
        return !CrVrScrCompositorIsEmpty(pCompositor);
    }

    int winVisibilityChanged()
    {
        HCR_FRAMEBUFFER hFb = getFramebuffer();
        if (!hFb || !CrFbIsEnabled(hFb))
        {
            Assert(!mpWindow || !mpWindow->IsVisivle());
            return VINF_SUCCESS;
        }

        int rc = VINF_SUCCESS;

        if (mpWindow)
        {
            rc = mpWindow->UpdateBegin();
            if (RT_SUCCESS(rc))
            {
                rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden);
                if (!RT_SUCCESS(rc))
                    WARN(("SetVisible failed, rc %d", rc));

                mpWindow->UpdateEnd();
            }
            else
                WARN(("UpdateBegin failed, rc %d", rc));
        }

        return rc;
    }

    CrFbWindow* getWindow() {return mpWindow;}
protected:
    virtual void onUpdateEnd()
    {
        CrFbDisplayBase::onUpdateEnd();
        bool fVisible = isVisible();
        if (mFlags.fNeVisible != fVisible || mFlags.fNeForce)
        {
            crVBoxServerNotifyEvent(mu32Screen, VBOX3D_NOTIFY_EVENT_TYPE_VISIBLE_3DDATA, fVisible ? (void*)1 : NULL);
            mFlags.fNeVisible = fVisible;
            mFlags.fNeForce = 0;
        }
    }

    virtual void ueRegions()
    {
        if (mpWindow)
            mpWindow->SetVisibleRegionsChanged();
    }

    virtual int screenChanged()
    {
        if (!isUpdating())
        {
            WARN(("not updating!"));
            return VERR_INVALID_STATE;
        }

        int rc = windowDimensionsSync();
        if (!RT_SUCCESS(rc))
        {
            WARN(("windowDimensionsSync failed rc %d", rc));
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual int windowSetCompositor(bool fSet)
    {
        if (!mpWindow)
            return VINF_SUCCESS;

        if (fSet)
        {
            const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer());
            return mpWindow->SetCompositor(pCompositor);
        }
        return mpWindow->SetCompositor(NULL);
    }

    virtual int windowCleanup()
    {
        if (!mpWindow)
            return VINF_SUCCESS;

        int rc = mpWindow->UpdateBegin();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        rc = windowDimensionsSync(true);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            mpWindow->UpdateEnd();
            return rc;
        }

        rc = windowSetCompositor(false);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            mpWindow->UpdateEnd();
            return rc;
        }

        mpWindow->UpdateEnd();

        return VINF_SUCCESS;
    }

    virtual int fbCleanup()
    {
        int rc = windowCleanup();
        if (!RT_SUCCESS(rc))
        {
            WARN(("windowCleanup failed"));
            return rc;
        }
        return CrFbDisplayBase::fbCleanup();
    }

    bool isActive()
    {
        HCR_FRAMEBUFFER hFb = getFramebuffer();
        return hFb && CrFbIsEnabled(hFb);
    }

    int windowDimensionsSync(bool fForceCleanup = false)
    {
        int rc = VINF_SUCCESS;

        if (!mpWindow)
            return VINF_SUCCESS;

//        HCR_FRAMEBUFFER hFb = getFramebuffer();
        if (!fForceCleanup && isActive())
        {
            const RTRECT* pRect = getRect();

            if (mpWindow->GetParentId() != mParentId)
            {
                rc = mpWindow->Reparent(mParentId);
                if (!RT_SUCCESS(rc))
                {
                    WARN(("err"));
                    return rc;
                }
            }

            rc = mpWindow->SetPosition(pRect->xLeft - mViewportRect.xLeft, pRect->yTop - mViewportRect.yTop);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }

            setRegionsChanged();

            rc = mpWindow->SetSize((uint32_t)(pRect->xRight - pRect->xLeft), (uint32_t)(pRect->yBottom - pRect->yTop));
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }

            rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
        }
        else
        {
            rc = mpWindow->SetVisible(false);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }

#if 0
            rc = mpWindow->Reparent(mDefaultParentId);
            if (!RT_SUCCESS(rc))
            {
                WARN(("err"));
                return rc;
            }
#endif
        }

        return rc;
    }

    virtual int windowSync()
    {
        if (!mpWindow)
            return VINF_SUCCESS;

        int rc = mpWindow->UpdateBegin();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        rc = windowSetCompositor(true);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            mpWindow->UpdateEnd();
            return rc;
        }

        rc = windowDimensionsSync();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            mpWindow->UpdateEnd();
            return rc;
        }

        mpWindow->UpdateEnd();

        return rc;
    }

    virtual int fbSync()
    {
        int rc = CrFbDisplayBase::fbSync();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        HCR_FRAMEBUFFER hFb = getFramebuffer();

        mu32Screen = CrFbGetScreenInfo(hFb)->u32ViewIndex;

        rc = windowSync();
        if (!RT_SUCCESS(rc))
        {
            WARN(("windowSync failed %d", rc));
            return rc;
        }

        if (CrFbHas3DData(hFb))
        {
            if (mpWindow && mpWindow->GetParentId())
            {
                rc = mpWindow->Create();
                if (!RT_SUCCESS(rc))
                {
                    WARN(("err"));
                    return rc;
                }
            }
        }

        return VINF_SUCCESS;
    }

    virtual const struct RTRECT* getRect()
    {
        const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer());
        return CrVrScrCompositorRectGet(pCompositor);
    }

private:
    CrFbWindow *mpWindow;
    RTRECT mViewportRect;
    CR_FBDISPWINDOW_FLAGS mFlags;
    uint32_t mu32Screen;
    uint64_t mParentId;
    uint64_t mDefaultParentId;
};

class CrFbDisplayWindowRootVr : public CrFbDisplayWindow
{
public:
    CrFbDisplayWindowRootVr(const RTRECT *pViewportRect, uint64_t parentId, uint64_t defaultParentId) :
        CrFbDisplayWindow(pViewportRect, parentId, defaultParentId)
    {
        CrVrScrCompositorInit(&mCompositor, NULL);
    }

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayWindow::EntryCreated(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        Assert(!CrFbDDataEntryGet(hEntry, slotGet()));

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = entryAlloc();
        CrVrScrCompositorEntryInit(pMyEntry, CrVrScrCompositorEntryRectGet(pSrcEntry), CrVrScrCompositorEntryTexGet(pSrcEntry), NULL);
        CrVrScrCompositorEntryFlagsSet(pMyEntry, CrVrScrCompositorEntryFlagsGet(pSrcEntry));
        rc = CrFbDDataEntryPut(hEntry, slotGet(), pMyEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrFbDDataEntryPut failed rc %d", rc));
            entryFree(pMyEntry);
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayWindow::EntryAdded(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
        Assert(pMyEntry);
        CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry));

        return VINF_SUCCESS;
    }

    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        int rc = CrFbDisplayWindow::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcNewEntry = CrFbEntryGetCompositorEntry(hNewEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hNewEntry, slotGet());
        CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcNewEntry));

        return VINF_SUCCESS;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayWindow::EntryTexChanged(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
        CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry));

        return VINF_SUCCESS;
    }

    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayWindow::EntryRemoved(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
        rc = CrVrScrCompositorEntryRegionsSet(&mCompositor, pMyEntry, NULL, 0, NULL, false, NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayWindow::EntryDestroyed(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet());
        CrVrScrCompositorEntryCleanup(pMyEntry);
        entryFree(pMyEntry);

        return VINF_SUCCESS;
    }

    virtual int setViewportRect(const RTRECT *pViewportRect)
    {
        int rc = CrFbDisplayWindow::setViewportRect(pViewportRect);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        rc = setRegionsChanged();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return VINF_SUCCESS;
    }

protected:
    virtual int windowSetCompositor(bool fSet)
    {
        if (fSet)
            return getWindow()->SetCompositor(&mCompositor);
        return getWindow()->SetCompositor(NULL);
    }

    virtual void ueRegions()
    {
        synchCompositorRegions();
    }

    int compositorMarkUpdated()
    {
        CrVrScrCompositorClear(&mCompositor);

        int rc = CrVrScrCompositorRectSet(&mCompositor, CrVrScrCompositorRectGet(CrFbGetCompositor(getFramebuffer())), NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        rc = setRegionsChanged();
        if (!RT_SUCCESS(rc))
        {
            WARN(("screenChanged failed %d", rc));
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual int screenChanged()
    {
        int rc = compositorMarkUpdated();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        rc = CrFbDisplayWindow::screenChanged();
        if (!RT_SUCCESS(rc))
        {
            WARN(("screenChanged failed %d", rc));
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual const struct RTRECT* getRect()
    {
        return CrVrScrCompositorRectGet(&mCompositor);
    }

    virtual int fbCleanup()
    {
        int rc = clearCompositor();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return CrFbDisplayWindow::fbCleanup();
    }

    virtual int fbSync()
    {
        int rc = synchCompositor();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return CrFbDisplayWindow::fbSync();
    }

    VBOXVR_SCR_COMPOSITOR_ENTRY* entryAlloc()
    {
#ifndef VBOXVDBG_MEMCACHE_DISABLE
        return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemCacheAlloc(g_CrPresenter.CEntryLookasideList);
#else
        return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemAlloc(sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY));
#endif
    }

    void entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry)
    {
        Assert(!CrVrScrCompositorEntryIsUsed(pEntry));
#ifndef VBOXVDBG_MEMCACHE_DISABLE
        RTMemCacheFree(g_CrPresenter.CEntryLookasideList, pEntry);
#else
        RTMemFree(pEntry);
#endif
    }

    int synchCompositorRegions()
    {
        int rc;

        rootVrTranslateForPos();

        /* ensure the rootvr compositor does not hold any data,
         * i.e. cleanup all rootvr entries data */
        CrVrScrCompositorClear(&mCompositor);

        rc = CrVrScrCompositorIntersectedList(CrFbGetCompositor(getFramebuffer()), &cr_server.RootVr, &mCompositor, rootVrGetCEntry, this, NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrVrScrCompositorIntersectedList failed, rc %d", rc));
            return rc;
        }

        return getWindow()->SetVisibleRegionsChanged();
    }

    virtual int synchCompositor()
    {
        int rc = compositorMarkUpdated();
        if (!RT_SUCCESS(rc))
        {
            WARN(("compositorMarkUpdated failed, rc %d", rc));
            return rc;
        }

        rc = fbSynchAddAllEntries();
        if (!RT_SUCCESS(rc))
        {
            WARN(("fbSynchAddAllEntries failed, rc %d", rc));
            return rc;
        }

        return rc;
    }

    virtual int clearCompositor()
    {
        return fbCleanupRemoveAllEntries();
    }

    void rootVrTranslateForPos()
    {
        const RTRECT *pRect = getViewportRect();
        const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(getFramebuffer());
        int32_t x = pScreen->i32OriginX;
        int32_t y = pScreen->i32OriginY;
        int32_t dx = cr_server.RootVrCurPoint.x - x;
        int32_t dy = cr_server.RootVrCurPoint.y - y;

        cr_server.RootVrCurPoint.x = x;
        cr_server.RootVrCurPoint.y = y;

        VBoxVrListTranslate(&cr_server.RootVr, dx, dy);
    }

    static DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext)
    {
        CrFbDisplayWindowRootVr *pThis = (CrFbDisplayWindowRootVr*)pvContext;
        HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
        VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, pThis->slotGet());
        Assert(!CrVrScrCompositorEntryIsUsed(pMyEntry));
        CrVrScrCompositorEntryRectSet(&pThis->mCompositor, pMyEntry, CrVrScrCompositorEntryRectGet(pEntry));
        return pMyEntry;
    }
private:
    VBOXVR_SCR_COMPOSITOR mCompositor;
};

class CrFbDisplayVrdp : public CrFbDisplayBase
{
public:
    CrFbDisplayVrdp()
    {
        memset(&mPos, 0, sizeof (mPos));
    }

    virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("EntryAdded failed rc %d", rc));
            return rc;
        }

        Assert(!CrFbDDataEntryGet(hEntry, slotGet()));
        rc = vrdpCreate(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("vrdpCreate failed rc %d", rc));
            return rc;
        }

        return VINF_SUCCESS;
    }

    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pReplacedEntry = CrFbEntryGetCompositorEntry(hReplacedEntry);
        CR_TEXDATA *pReplacedTex = CrVrScrCompositorEntryTexGet(pReplacedEntry);
        const VBOXVR_SCR_COMPOSITOR_ENTRY* pNewEntry = CrFbEntryGetCompositorEntry(hNewEntry);
        CR_TEXDATA *pNewTex = CrVrScrCompositorEntryTexGet(pNewEntry);

        CrTdBltDataInvalidateNe(pReplacedTex);

        rc = CrTdBltEnter(pNewTex);
        if (RT_SUCCESS(rc))
        {
            rc = vrdpFrame(hNewEntry);
            CrTdBltLeave(pNewTex);
        }
        else
            WARN(("CrTdBltEnter failed %d", rc));

        return rc;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
        CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);

        rc = CrTdBltEnter(pTex);
        if (RT_SUCCESS(rc))
        {
            rc = vrdpFrame(hEntry);
            CrTdBltLeave(pTex);
        }
        else
            WARN(("CrTdBltEnter failed %d", rc));

    	return rc;
    }

    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
        CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
        CrTdBltDataInvalidateNe(pTex);

        return vrdpRegions(pFb, hEntry);
    }

    virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        vrdpDestroy(hEntry);
        return VINF_SUCCESS;
    }

    virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        int rc = CrFbDisplayBase::EntryPosChanged(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        vrdpGeometry(hEntry);

        return VINF_SUCCESS;
    }

    virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::RegionsChanged(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return vrdpRegionsAll(pFb);
    }

    virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
    {
        int rc = CrFbDisplayBase::FramebufferChanged(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        syncPos();

        rc = vrdpSyncEntryAll(pFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return vrdpRegionsAll(pFb);
    }

protected:
    void syncPos()
    {
        const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(getFramebuffer());
        mPos.x = pScreenInfo->i32OriginX;
        mPos.y = pScreenInfo->i32OriginY;
    }

    virtual int fbCleanup()
    {
        int rc = fbCleanupRemoveAllEntries();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return CrFbDisplayBase::fbCleanup();
    }

    virtual int fbSync()
    {
        syncPos();

        int rc = fbSynchAddAllEntries();
        if (!RT_SUCCESS(rc))
        {
            WARN(("err"));
            return rc;
        }

        return CrFbDisplayBase::fbSync();
    }
protected:
    void vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
        cr_server.outputRedirect.CROREnd(pVrdp);
    }

    void vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry)
    {
    	void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
        const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);

        cr_server.outputRedirect.CRORGeometry(pVrdp,
        										mPos.x + CrVrScrCompositorEntryRectGet(pEntry)->xLeft,
        										mPos.y + CrVrScrCompositorEntryRectGet(pEntry)->yTop,
        									   CrVrScrCompositorEntryTexGet(pEntry)->Tex.width,
                                               CrVrScrCompositorEntryTexGet(pEntry)->Tex.height);
    }

    int vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
    	const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
        const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
        uint32_t cRects;
        const RTRECT *pRects;

        int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, &pRects, NULL, NULL);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc));
            return rc;
        }

        cr_server.outputRedirect.CRORVisibleRegion(pVrdp, cRects, pRects);
        return VINF_SUCCESS;
    }

    int vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
        const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
    	CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
    	const CR_BLITTER_IMG *pImg;
    	CrTdBltDataInvalidateNe(pTex);
    	int rc = CrTdBltDataAcquire(pTex, GL_BGRA, !!(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS), &pImg);
    	if (!RT_SUCCESS(rc))
    	{
    		WARN(("CrTdBltDataAcquire failed rc %d", rc));
    		return rc;
    	}

        cr_server.outputRedirect.CRORFrame(pVrdp, pImg->pvData, pImg->cbData);
        CrTdBltDataRelease(pTex);
        return VINF_SUCCESS;
    }

    int vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb)
    {
    	const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
        VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
        CrVrScrCompositorConstIterInit(pCompositor, &Iter);
        const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
        while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
        {
        	HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
        	vrdpRegions(pFb, hEntry);
        }

        return VINF_SUCCESS;
    }

    int vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        vrdpGeometry(hEntry);

        return vrdpRegions(pFb, hEntry);;
    }

    int vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb)
    {
        const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
        VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
        CrVrScrCompositorConstIterInit(pCompositor, &Iter);
        const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
        while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
        {
            HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
            int rc = vrdpSynchEntry(pFb, hEntry);
            if (!RT_SUCCESS(rc))
            {
                WARN(("vrdpSynchEntry failed rc %d", rc));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    int vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
    	void *pVrdp;

        /* Query supported formats. */
        uint32_t cbFormats = 4096;
        char *pachFormats = (char *)crAlloc(cbFormats);

        if (!pachFormats)
        {
            WARN(("crAlloc failed"));
            return VERR_NO_MEMORY;
        }

        int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext,
                                                                  0 /* H3DOR_PROP_FORMATS */, // @todo from a header
                                                                  pachFormats, cbFormats, &cbFormats);
        if (RT_SUCCESS(rc))
        {
            if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN"))
            {
                cr_server.outputRedirect.CRORBegin(cr_server.outputRedirect.pvContext,
                		        &pVrdp,
                                "H3DOR_FMT_RGBA_TOPDOWN"); // @todo from a header

                if (pVrdp)
                {
                    rc = CrFbDDataEntryPut(hEntry, slotGet(), pVrdp);
                    if (RT_SUCCESS(rc))
                    {
                    	vrdpGeometry(hEntry);
                    	vrdpRegions(hFb, hEntry);
                    	//vrdpFrame(hEntry);
                        return VINF_SUCCESS;
                    }
                    else
                    	WARN(("CrFbDDataEntryPut failed rc %d", rc));

                    cr_server.outputRedirect.CROREnd(pVrdp);
                }
                else
                {
                    WARN(("CRORBegin failed"));
                    rc = VERR_GENERAL_FAILURE;
                }
            }
        }
        else
            WARN(("CRORContextProperty failed rc %d", rc));

        crFree(pachFormats);

        return rc;
    }
private:
    RTPOINT mPos;
};

CrFbDisplayBase::~CrFbDisplayBase()
{
    Assert(!mcUpdates);

    if (mpContainer)
        mpContainer->remove(this);
}


#if 0





void crDbgDumpRect(uint32_t i, const RTRECT *pRect)
{
    crDebug("%d: (%d;%d) X (%d;%d)", i, pRect->xLeft, pRect->yTop, pRect->xRight, pRect->yBottom);
}

void crDbgDumpRects(uint32_t cRects, const RTRECT *paRects)
{
    crDebug("Dumping rects (%d)", cRects);
    for (uint32_t i = 0; i < cRects; ++i)
    {
        crDbgDumpRect(i, &paRects[i]);
    }
    crDebug("End Dumping rects (%d)", cRects);
}

#endif

class CrFbDisplayEntryDataMonitor : public CrFbDisplayBase
{
public:
    virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
    {
        entryDataChanged(pFb, hReplacedEntry);
        return VINF_SUCCESS;
    }

    virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        entryDataChanged(pFb, hEntry);
        return VINF_SUCCESS;
    }

    virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {
        entryDataChanged(pFb, hEntry);
        return VINF_SUCCESS;
    }
protected:
    virtual void entryDataChanged(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry)
    {

    }
};

int CrPMgrDisable()
{
    if (!g_CrPresenter.fEnabled)
        return VINF_SUCCESS;

    g_CrPresenter.u32DisabledDisplayMode = g_CrPresenter.u32DisplayMode;

    int rc = crPMgrModeModifyGlobal(0, CR_PMGR_MODE_WINDOW);
    if (RT_FAILURE(rc))
    {
        WARN(("crPMgrModeModifyGlobal failed %d", rc));
        return rc;
    }

    crPMgrCleanUnusedDisplays();

    g_CrPresenter.fEnabled = false;

    return VINF_SUCCESS;
}

int CrPMgrEnable()
{
    if (g_CrPresenter.fEnabled)
        return VINF_SUCCESS;

    g_CrPresenter.fEnabled = true;

    int rc = crPMgrModeModifyGlobal(g_CrPresenter.u32DisabledDisplayMode, 0);
    if (RT_FAILURE(rc))
    {
        WARN(("crPMgrModeModifyGlobal failed %d", rc));
        g_CrPresenter.fEnabled = false;
        return rc;
    }

    g_CrPresenter.u32DisabledDisplayMode = 0;

    return VINF_SUCCESS;
}

int CrPMgrInit()
{
    int rc = VINF_SUCCESS;
    memset(&g_CrPresenter, 0, sizeof (g_CrPresenter));
    g_CrPresenter.fEnabled = true;
    for (int i = 0; i < RT_ELEMENTS(g_CrPresenter.aDisplayInfos); ++i)
    {
        g_CrPresenter.aDisplayInfos[i].u32Id = i;
        g_CrPresenter.aDisplayInfos[i].iFb = -1;

        g_CrPresenter.aFbInfos[i].u32Id = i;
    }

    g_CrPresenter.pFbTexMap = crAllocHashtable();
    if (g_CrPresenter.pFbTexMap)
    {
#ifndef VBOXVDBG_MEMCACHE_DISABLE
        rc = RTMemCacheCreate(&g_CrPresenter.FbEntryLookasideList, sizeof (CR_FRAMEBUFFER_ENTRY),
                                0, /* size_t cbAlignment */
                                UINT32_MAX, /* uint32_t cMaxObjects */
                                NULL, /* PFNMEMCACHECTOR pfnCtor*/
                                NULL, /* PFNMEMCACHEDTOR pfnDtor*/
                                NULL, /* void *pvUser*/
                                0 /* uint32_t fFlags*/
                                );
        if (RT_SUCCESS(rc))
        {
            rc = RTMemCacheCreate(&g_CrPresenter.FbTexLookasideList, sizeof (CR_FBTEX),
                                        0, /* size_t cbAlignment */
                                        UINT32_MAX, /* uint32_t cMaxObjects */
                                        NULL, /* PFNMEMCACHECTOR pfnCtor*/
                                        NULL, /* PFNMEMCACHEDTOR pfnDtor*/
                                        NULL, /* void *pvUser*/
                                        0 /* uint32_t fFlags*/
                                        );
            if (RT_SUCCESS(rc))
            {
                rc = RTMemCacheCreate(&g_CrPresenter.CEntryLookasideList, sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY),
                                            0, /* size_t cbAlignment */
                                            UINT32_MAX, /* uint32_t cMaxObjects */
                                            NULL, /* PFNMEMCACHECTOR pfnCtor*/
                                            NULL, /* PFNMEMCACHEDTOR pfnDtor*/
                                            NULL, /* void *pvUser*/
                                            0 /* uint32_t fFlags*/
                                            );
                if (RT_SUCCESS(rc))
                {
#endif
                    rc = crPMgrModeModifyGlobal(CR_PMGR_MODE_WINDOW, 0);
                    if (RT_SUCCESS(rc))
                        return VINF_SUCCESS;
                    else
                        WARN(("crPMgrModeModifyGlobal failed rc %d", rc));
#ifndef VBOXVDBG_MEMCACHE_DISABLE
                    RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList);
                }
                else
                    WARN(("RTMemCacheCreate failed rc %d", rc));

                RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList);
            }
            else
                WARN(("RTMemCacheCreate failed rc %d", rc));

            RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList);
        }
        else
            WARN(("RTMemCacheCreate failed rc %d", rc));
#endif
    }
    else
    {
        WARN(("crAllocHashtable failed"));
        rc = VERR_NO_MEMORY;
    }
    return rc;
}

void CrPMgrTerm()
{
    crPMgrModeModifyGlobal(0, CR_PMGR_MODE_ALL);

    HCR_FRAMEBUFFER hFb;

    for (hFb = CrPMgrFbGetFirstInitialized();
            hFb;
            hFb = CrPMgrFbGetNextInitialized(hFb))
    {
        uint32_t iFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
        CrFbDisplaySet(hFb, NULL);
        CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[iFb];
        if (pFbInfo->pDpComposite)
        {
            delete pFbInfo->pDpComposite;
            pFbInfo->pDpComposite = NULL;
        }

        CrFbTerm(hFb);
    }

    crPMgrCleanUnusedDisplays();

#ifndef VBOXVDBG_MEMCACHE_DISABLE
    RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList);
    RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList);
    RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList);
#endif
    crFreeHashtable(g_CrPresenter.pFbTexMap, NULL);

    if (g_CrPresenter.pvTmpBuf)
        RTMemFree(g_CrPresenter.pvTmpBuf);

    if (g_CrPresenter.pvTmpBuf2)
        RTMemFree(g_CrPresenter.pvTmpBuf2);

    memset(&g_CrPresenter, 0, sizeof (g_CrPresenter));
}

HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idFb)
{
    if (idFb >= CR_MAX_GUEST_MONITORS)
    {
        WARN(("invalid idFb %d", idFb));
        return NULL;
    }

    if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb))
    {
        CrFbInit(&g_CrPresenter.aFramebuffers[idFb], idFb);
        CrFBmSetAtomic(&g_CrPresenter.FramebufferInitMap, idFb);
    }
    else
        Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb);

    return &g_CrPresenter.aFramebuffers[idFb];
}

HCR_FRAMEBUFFER CrPMgrFbGetInitialized(uint32_t idFb)
{
    if (idFb >= CR_MAX_GUEST_MONITORS)
    {
        WARN(("invalid idFb %d", idFb));
        return NULL;
    }

    if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb))
    {
        return NULL;
    }
    else
        Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb);

    return &g_CrPresenter.aFramebuffers[idFb];
}

HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idFb)
{
    HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(idFb);

    if(hFb && CrFbIsEnabled(hFb))
        return hFb;

    return NULL;
}

HCR_FRAMEBUFFER CrPMgrFbGetEnabledForScreen(uint32_t idScreen)
{
    if (idScreen >= (uint32_t)cr_server.screenCount)
    {
        WARN(("invalid target id"));
        return NULL;
    }

    const CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
    if (pDpInfo->iFb < 0)
        return NULL;

    return CrPMgrFbGetEnabled(pDpInfo->iFb);
}

static HCR_FRAMEBUFFER crPMgrFbGetNextEnabled(uint32_t i)
{
    for (;i < (uint32_t)cr_server.screenCount; ++i)
    {
        HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(i);
        if (hFb)
            return hFb;
    }

    return NULL;
}

static HCR_FRAMEBUFFER crPMgrFbGetNextInitialized(uint32_t i)
{
    for (;i < (uint32_t)cr_server.screenCount; ++i)
    {
        HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i);
        if (hFb)
            return hFb;
    }

    return NULL;
}

HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled()
{
    HCR_FRAMEBUFFER hFb = crPMgrFbGetNextEnabled(0);
//    if (!hFb)
//        WARN(("no enabled framebuffer found"));
    return hFb;
}

HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb)
{
    return crPMgrFbGetNextEnabled(hFb->ScreenInfo.u32ViewIndex+1);
}

HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized()
{
    HCR_FRAMEBUFFER hFb = crPMgrFbGetNextInitialized(0);
//    if (!hFb)
//        WARN(("no initialized framebuffer found"));
    return hFb;
}

HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb)
{
    return crPMgrFbGetNextInitialized(hFb->ScreenInfo.u32ViewIndex+1);
}

HCR_FRAMEBUFFER CrPMgrFbGetEnabledByVramStart(VBOXCMDVBVAOFFSET offVRAM)
{
    for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
            hFb;
            hFb = CrPMgrFbGetNextEnabled(hFb))
    {
        const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
        if (pScreen->u32StartOffset == offVRAM)
            return hFb;
    }

    return NULL;
}


static uint32_t crPMgrModeAdjustVal(uint32_t u32Mode)
{
    u32Mode = CR_PMGR_MODE_ALL & u32Mode;
    if (CR_PMGR_MODE_ROOTVR & u32Mode)
        u32Mode &= ~CR_PMGR_MODE_WINDOW;
    return u32Mode;
}

static int crPMgrCheckInitWindowDisplays(uint32_t idScreen)
{
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
    CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
    if (pDpInfo->iFb >= 0)
    {
        uint32_t u32ModeAdd = g_CrPresenter.u32DisplayMode & (CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR);
        int rc = crPMgrFbConnectTargetDisplays(&g_CrPresenter.aFramebuffers[pDpInfo->iFb], pDpInfo, u32ModeAdd);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
            return rc;
        }
    }
#endif
    return VINF_SUCCESS;
}

int CrPMgrScreenChanged(uint32_t idScreen)
{
    if (idScreen >= CR_MAX_GUEST_MONITORS)
    {
        WARN(("invalid idScreen %d", idScreen));
        return VERR_INVALID_PARAMETER;
    }

    int rc = VINF_SUCCESS;
    CR_FBDISPLAY_INFO *pDpInfo;
#if 0
    bool fDefaultParentChange = (idScreen == 0);
    if (fDefaultParentChange)
    {
        for (int i = 0; i < cr_server.screenCount; ++i)
        {
            pDpInfo = &g_CrPresenter.aDisplayInfos[i];
            if (pDpInfo->pDpWin)
            {
                HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL;
                rc = pDpInfo->pDpWin->UpdateBegin(hFb);
                if (RT_SUCCESS(rc))
                {
                    rc = pDpInfo->pDpWin->setDefaultParent(cr_server.screen[idScreen].winID);
                    if (RT_FAILURE(rc))
                    {
                        WARN(("setDefaultParent failed %d", rc));
                        pDpInfo->pDpWin->UpdateEnd(hFb);
                    }
                }
                else
                    WARN(("UpdateBegin failed %d", rc));

                if (RT_FAILURE(rc))
                {
                    WARN(("err"));
                    for (int j = 0; j < i - 1; ++j)
                    {
                        pDpInfo = &g_CrPresenter.aDisplayInfos[j];
                        if (pDpInfo->pDpWin)
                        {
                            HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL;
                            pDpInfo->pDpWin->UpdateEnd(hFb);
                        }
                    }

                    Assert(RT_FAILURE(rc));
                    return rc;
                }
            }
        }
    }
#endif

    pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];

    HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL;
    if (hFb && CrFbIsUpdating(hFb))
    {
        WARN(("trying to update viewport while framebuffer is being updated"));
        rc = VERR_INVALID_STATE;
        goto end;
    }

    if (pDpInfo->pDpWin)
    {
        CRASSERT(pDpInfo->pDpWin->getWindow());

        rc = pDpInfo->pDpWin->UpdateBegin(hFb);
        if (RT_SUCCESS(rc))
        {
            pDpInfo->pDpWin->reparent(cr_server.screen[idScreen].winID);

            pDpInfo->pDpWin->UpdateEnd(hFb);
        }
        else
            WARN(("UpdateBegin failed %d", rc));
    }
    else
    {
        if (pDpInfo->pWindow)
        {
            rc = pDpInfo->pWindow->UpdateBegin();
            if (RT_FAILURE(rc))
            {
                WARN(("UpdateBegin failed %d", rc));
                goto end;
            }

            rc = pDpInfo->pWindow->SetVisible(false);
            if (RT_FAILURE(rc))
            {
                WARN(("SetVisible failed %d", rc));
                pDpInfo->pWindow->UpdateEnd();
                goto end;
            }

            rc = pDpInfo->pWindow->Reparent(cr_server.screen[idScreen].winID);
            if (RT_FAILURE(rc))
            {
                WARN(("Reparent failed %d", rc));
                pDpInfo->pWindow->UpdateEnd();
                goto end;
            }

            pDpInfo->pWindow->UpdateEnd();
        }

        rc = crPMgrCheckInitWindowDisplays(idScreen);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
            goto end;
        }
    }
end:
#if 0
    if (fDefaultParentChange)
    {
        for (int i = 0; i < cr_server.screenCount; ++i)
        {
            pDpInfo = &g_CrPresenter.aDisplayInfos[i];
            if (pDpInfo->pDpWin)
            {
                HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL;
                pDpInfo->pDpWin->UpdateEnd(hFb);
            }
        }
    }
#endif
    return rc;
}

int CrPMgrViewportUpdate(uint32_t idScreen)
{
    if (idScreen >= CR_MAX_GUEST_MONITORS)
    {
        WARN(("invalid idScreen %d", idScreen));
        return VERR_INVALID_PARAMETER;
    }

    CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen];
    if (pDpInfo->iFb >= 0)
    {
        HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pDpInfo->iFb);
        if (CrFbIsUpdating(hFb))
        {
            WARN(("trying to update viewport while framebuffer is being updated"));
            return VERR_INVALID_STATE;
        }

        if (pDpInfo->pDpWin)
        {
            CRASSERT(pDpInfo->pDpWin->getWindow());
            int rc = pDpInfo->pDpWin->UpdateBegin(hFb);
            if (RT_SUCCESS(rc))
            {
                pDpInfo->pDpWin->setViewportRect(&cr_server.screenVieport[idScreen].Rect);
                pDpInfo->pDpWin->UpdateEnd(hFb);
            }
            else
                WARN(("UpdateBegin failed %d", rc));
        }
    }

    return VINF_SUCCESS;
}

static int crPMgrFbDisconnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp)
{
    if (pDp->getFramebuffer() != hFb)
        return VINF_SUCCESS;

    CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb);
    if (!pCurDp)
    {
        WARN(("no display set, unexpected"));
        return VERR_INTERNAL_ERROR;
    }

    if (pCurDp == pDp)
    {
        pDp->setFramebuffer(NULL);
        CrFbDisplaySet(hFb, NULL);
        return VINF_SUCCESS;
    }

    uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
    if (pFbInfo->pDpComposite != pCurDp)
    {
        WARN(("misconfig, expectig the curret framebuffer to be present, and thus composite is expected"));
        return VERR_INTERNAL_ERROR;
    }

    if (pDp->getContainer() == pFbInfo->pDpComposite)
    {
        pFbInfo->pDpComposite->remove(pDp);
        uint32_t cDisplays = pFbInfo->pDpComposite->getDisplayCount();
        if (cDisplays <= 1)
        {
            Assert(cDisplays == 1);
            CrFbDisplayBase *pDpFirst = pFbInfo->pDpComposite->first();
            if (pDpFirst)
                pFbInfo->pDpComposite->remove(pDpFirst, false);
            CrFbDisplaySet(hFb, pDpFirst);
        }
        return VINF_SUCCESS;
    }

    WARN(("misconfig"));
    return VERR_INTERNAL_ERROR;
}

static int crPMgrFbConnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp)
{
    if (pDp->getFramebuffer() == hFb)
        return VINF_SUCCESS;

    CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb);
    if (!pCurDp)
    {
        pDp->setFramebuffer(hFb);
        CrFbDisplaySet(hFb, pDp);
        return VINF_SUCCESS;
    }

    if (pCurDp == pDp)
    {
        WARN(("misconfig, current framebuffer is not expected to be set"));
        return VERR_INTERNAL_ERROR;
    }

    uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
    if (pFbInfo->pDpComposite != pCurDp)
    {
        if (!pFbInfo->pDpComposite)
        {
            pFbInfo->pDpComposite = new CrFbDisplayComposite();
            pFbInfo->pDpComposite->setFramebuffer(hFb);
        }

        pFbInfo->pDpComposite->add(pCurDp);
        CrFbDisplaySet(hFb, pFbInfo->pDpComposite);
    }

    pFbInfo->pDpComposite->add(pDp);
    return VINF_SUCCESS;
}

static int crPMgrFbDisconnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i)
{
    uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
    CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
    if (pDpInfo->iFb != idFb)
    {
        WARN(("target not connected"));
        Assert(!ASMBitTest(pFbInfo->aTargetMap, i));
        return VINF_SUCCESS;
    }

    Assert(ASMBitTest(pFbInfo->aTargetMap, i));

    int rc = VINF_SUCCESS;
    if (pDpInfo->pDpVrdp)
    {
        rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
            return rc;
        }
    }

    if (pDpInfo->pDpWinRootVr)
    {
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
        CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false);
        Assert(pWindow == pDpInfo->pWindow);
#endif
        rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
            return rc;
        }
    }
    else if (pDpInfo->pDpWin)
    {
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
        CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false);
        Assert(pWindow == pDpInfo->pWindow);
#endif
        rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnectDisplay failed %d", rc));
            return rc;
        }
    }

    ASMBitClear(pFbInfo->aTargetMap, i);
    pDpInfo->iFb = -1;

    return VINF_SUCCESS;
}

static void crPMgrDpWinRootVrCreate(CR_FBDISPLAY_INFO *pDpInfo)
{
    if (!pDpInfo->pDpWinRootVr)
    {
        if (pDpInfo->pDpWin)
        {
            CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach();
            CRASSERT(pWin);
            Assert(pWin == pDpInfo->pWindow);
            delete pDpInfo->pDpWin;
            pDpInfo->pDpWin = NULL;
        }
        else if (!pDpInfo->pWindow)
        {
            pDpInfo->pWindow = new CrFbWindow(0);
        }

        pDpInfo->pDpWinRootVr = new CrFbDisplayWindowRootVr(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID, /*cr_server.screen[0].winID*/ 0);
        pDpInfo->pDpWin = pDpInfo->pDpWinRootVr;
        pDpInfo->pDpWinRootVr->windowAttach(pDpInfo->pWindow);
    }
}

static void crPMgrDpWinCreate(CR_FBDISPLAY_INFO *pDpInfo)
{
    if (pDpInfo->pDpWinRootVr)
    {
        CRASSERT(pDpInfo->pDpWinRootVr == pDpInfo->pDpWin);
        CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach();
        CRASSERT(pWin);
        Assert(pWin == pDpInfo->pWindow);
        delete pDpInfo->pDpWinRootVr;
        pDpInfo->pDpWinRootVr = NULL;
        pDpInfo->pDpWin = NULL;
    }

    if (!pDpInfo->pDpWin)
    {
        if (!pDpInfo->pWindow)
            pDpInfo->pWindow = new CrFbWindow(0);

        pDpInfo->pDpWin = new CrFbDisplayWindow(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID, /*cr_server.screen[0].winID*/ 0);
        pDpInfo->pDpWin->windowAttach(pDpInfo->pWindow);
    }
}

static int crPMgrFbDisconnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeRemove)
{
    int rc = VINF_SUCCESS;
    if (u32ModeRemove & CR_PMGR_MODE_ROOTVR)
    {
        if (pDpInfo->pDpWinRootVr)
        {
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
            CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false);
            Assert(pWindow == pDpInfo->pWindow);
#endif
            CRASSERT(pDpInfo->pDpWin == pDpInfo->pDpWinRootVr);
            rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr);
            if (RT_FAILURE(rc))
            {
                WARN(("crPMgrFbDisconnectDisplay pDpWinRootVr failed %d", rc));
                return rc;
            }
        }
    }
    else if (u32ModeRemove & CR_PMGR_MODE_WINDOW)
    {
        CRASSERT(!pDpInfo->pDpWinRootVr);
        if (pDpInfo->pDpWin)
        {
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
            CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false);
            Assert(pWindow == pDpInfo->pWindow);
#endif
            rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin);
            if (RT_FAILURE(rc))
            {
                WARN(("crPMgrFbDisconnectDisplay pDpWin failed %d", rc));
                return rc;
            }
        }
    }

    if (u32ModeRemove & CR_PMGR_MODE_VRDP)
    {
        if (pDpInfo->pDpVrdp)
        {
            rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp);
            if (RT_FAILURE(rc))
            {
                WARN(("crPMgrFbDisconnectDisplay pDpVrdp failed %d", rc));
                return rc;
            }
        }
    }

    pDpInfo->u32DisplayMode &= ~u32ModeRemove;

    return VINF_SUCCESS;
}

static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd)
{
    int rc = VINF_SUCCESS;

    if (u32ModeAdd & CR_PMGR_MODE_ROOTVR)
    {
        crPMgrDpWinRootVrCreate(pDpInfo);

        rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWinRootVr);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectDisplay pDpWinRootVr failed %d", rc));
            return rc;
        }
    }
    else if (u32ModeAdd & CR_PMGR_MODE_WINDOW)
    {
        crPMgrDpWinCreate(pDpInfo);

        rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWin);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectDisplay pDpWin failed %d", rc));
            return rc;
        }
    }

    if (u32ModeAdd & CR_PMGR_MODE_VRDP)
    {
        if (!pDpInfo->pDpVrdp)
            pDpInfo->pDpVrdp = new CrFbDisplayVrdp();

        rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpVrdp);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectDisplay pDpVrdp failed %d", rc));
            return rc;
        }
    }

    pDpInfo->u32DisplayMode |= u32ModeAdd;

    return VINF_SUCCESS;
}

static int crPMgrFbConnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i)
{
    uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
    CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
    if (pDpInfo->iFb == idFb)
    {
        WARN(("target not connected"));
        Assert(ASMBitTest(pFbInfo->aTargetMap, i));
        return VINF_SUCCESS;
    }

    Assert(!ASMBitTest(pFbInfo->aTargetMap, i));

    int rc = VINF_SUCCESS;

    if (pDpInfo->iFb != -1)
    {
        Assert(pDpInfo->iFb < cr_server.screenCount);
        HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb);
        Assert(hAssignedFb);
        rc = crPMgrFbDisconnectTarget(hAssignedFb, i);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnectTarget failed %d", rc));
            return rc;
        }
    }

    rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, g_CrPresenter.u32DisplayMode
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
            & ~(CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR)
#endif
            );
    if (RT_FAILURE(rc))
    {
        WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
        return rc;
    }

    ASMBitSet(pFbInfo->aTargetMap, i);
    pDpInfo->iFb = idFb;

    return VINF_SUCCESS;
}

static int crPMgrFbDisconnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap)
{
    int rc = VINF_SUCCESS;
    for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
            i >= 0;
            i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
    {
        rc = crPMgrFbDisconnectTarget(hFb, (uint32_t)i);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnectTarget failed %d", rc));
            return rc;
        }
    }

    return VINF_SUCCESS;
}

static int crPMgrFbConnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap)
{
    int rc = VINF_SUCCESS;
    for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
            i >= 0;
            i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
    {
        rc = crPMgrFbConnectTarget(hFb, (uint32_t)i);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnectTarget failed %d", rc));
            return rc;
        }
    }

    return VINF_SUCCESS;
}

static int crPMgrModeModifyTarget(HCR_FRAMEBUFFER hFb, uint32_t iDisplay, uint32_t u32ModeAdd, uint32_t u32ModeRemove)
{
    CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[iDisplay];
    int rc = crPMgrFbDisconnectTargetDisplays(hFb, pDpInfo, u32ModeRemove);
    if (RT_FAILURE(rc))
    {
        WARN(("crPMgrFbDisconnectTargetDisplays failed %d", rc));
        return rc;
    }

    rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, u32ModeAdd);
    if (RT_FAILURE(rc))
    {
        WARN(("crPMgrFbConnectTargetDisplays failed %d", rc));
        return rc;
    }

    return VINF_SUCCESS;
}

static int crPMgrModeModify(HCR_FRAMEBUFFER hFb, uint32_t u32ModeAdd, uint32_t u32ModeRemove)
{
    int rc = VINF_SUCCESS;
    uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
    for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
            i >= 0;
            i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
    {
        rc = crPMgrModeModifyTarget(hFb, (uint32_t)i, u32ModeAdd, u32ModeRemove);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrModeModifyTarget failed %d", rc));
            return rc;
        }
    }

    return VINF_SUCCESS;
}

static void crPMgrCleanUnusedDisplays()
{
    for (int i = 0; i < cr_server.screenCount; ++i)
    {
        CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];

        if (pDpInfo->pDpWinRootVr)
        {
            if (!pDpInfo->pDpWinRootVr->getFramebuffer())
            {
                pDpInfo->pDpWinRootVr->windowDetach(false);
                delete pDpInfo->pDpWinRootVr;
                pDpInfo->pDpWinRootVr = NULL;
                pDpInfo->pDpWin = NULL;
                if (pDpInfo->pWindow)
                {
                    delete pDpInfo->pWindow;
                    pDpInfo->pWindow = NULL;
                }
            }
            else
                WARN(("pDpWinRootVr is used"));
        }
        else if (pDpInfo->pDpWin)
        {
            if (!pDpInfo->pDpWin->getFramebuffer())
            {
                pDpInfo->pDpWin->windowDetach(false);
                delete pDpInfo->pDpWin;
                pDpInfo->pDpWin = NULL;
                if (pDpInfo->pWindow)
                {
                    delete pDpInfo->pWindow;
                    pDpInfo->pWindow = NULL;
                }
            }
            else
                WARN(("pDpWin is used"));
        }

        if (pDpInfo->pDpVrdp)
        {
            if (!pDpInfo->pDpVrdp->getFramebuffer())
            {
                delete pDpInfo->pDpVrdp;
                pDpInfo->pDpVrdp = NULL;
            }
            else
                WARN(("pDpVrdp is used"));
        }
    }
}

static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove)
{
    uint32_t u32InternalMode = g_CrPresenter.fEnabled ? g_CrPresenter.u32DisplayMode : g_CrPresenter.u32DisabledDisplayMode;

    u32ModeRemove = ((u32ModeRemove | crPMgrModeAdjustVal(u32ModeRemove)) & CR_PMGR_MODE_ALL);
    u32ModeAdd = crPMgrModeAdjustVal(u32ModeAdd);
    u32ModeRemove &= u32InternalMode;
    u32ModeAdd &= ~(u32ModeRemove | u32InternalMode);
    uint32_t u32ModeResulting = ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove);
    uint32_t u32Tmp = crPMgrModeAdjustVal(u32ModeResulting);
    if (u32Tmp != u32ModeResulting)
    {
        u32ModeAdd |= (u32Tmp & ~u32ModeResulting);
        u32ModeRemove |= (~u32Tmp & u32ModeResulting);
        u32ModeResulting = u32Tmp;
        Assert(u32ModeResulting == ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove));
    }
    if (!u32ModeRemove && !u32ModeAdd)
        return VINF_SUCCESS;

    uint32_t u32DisplayMode = (g_CrPresenter.u32DisplayMode | u32ModeAdd) & ~u32ModeRemove;
    if (!g_CrPresenter.fEnabled)
    {
        Assert(g_CrPresenter.u32DisplayMode == 0);
        g_CrPresenter.u32DisabledDisplayMode = u32DisplayMode;
        return VINF_SUCCESS;
    }

    g_CrPresenter.u32DisplayMode = u32DisplayMode;

    /* disabled framebuffers may still have displays attached */
    for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstInitialized();
            hFb;
            hFb = CrPMgrFbGetNextInitialized(hFb))
    {
        crPMgrModeModify(hFb, u32ModeAdd, u32ModeRemove);
    }

    return VINF_SUCCESS;
}

int CrPMgrClearRegionsGlobal()
{
    for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
            hFb;
            hFb = CrPMgrFbGetNextEnabled(hFb))
    {
        int rc = CrFbUpdateBegin(hFb);
        if (RT_SUCCESS(rc))
        {
            rc = CrFbRegionsClear(hFb);
            if (RT_FAILURE(rc))
            {
                WARN(("CrFbRegionsClear failed %d", rc));
            }

            CrFbUpdateEnd(hFb);
        }
    }

    return VINF_SUCCESS;
}

int CrPMgrModeVrdp(bool fEnable)
{
    uint32_t u32ModeAdd, u32ModeRemove;
    if (fEnable)
    {
        u32ModeAdd = CR_PMGR_MODE_VRDP;
        u32ModeRemove = 0;
    }
    else
    {
        u32ModeAdd = 0;
        u32ModeRemove = CR_PMGR_MODE_VRDP;
    }
    return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove);
}

int CrPMgrModeRootVr(bool fEnable)
{
    uint32_t u32ModeAdd, u32ModeRemove;
    if (fEnable)
    {
        u32ModeAdd = CR_PMGR_MODE_ROOTVR;
        u32ModeRemove = CR_PMGR_MODE_WINDOW;
    }
    else
    {
        u32ModeAdd = CR_PMGR_MODE_WINDOW;
        u32ModeRemove = CR_PMGR_MODE_ROOTVR;
    }

    return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove);
}

int CrPMgrModeWinVisible(bool fEnable)
{
    if (!g_CrPresenter.fWindowsForceHidden == !!fEnable)
        return VINF_SUCCESS;

    g_CrPresenter.fWindowsForceHidden = !fEnable;

    for (int i = 0; i < cr_server.screenCount; ++i)
    {
        CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];

        if (pDpInfo->pDpWin)
            pDpInfo->pDpWin->winVisibilityChanged();
    }

    return VINF_SUCCESS;
}

int CrPMgrRootVrUpdate()
{
    for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
            hFb;
            hFb = CrPMgrFbGetNextEnabled(hFb))
    {
        if (!CrFbHas3DData(hFb))
            continue;

        uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex;
        CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];
        int rc = CrFbUpdateBegin(hFb);
        if (RT_SUCCESS(rc))
        {
            for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
                    i >= 0;
                    i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
            {
                CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
                Assert(pDpInfo->iFb == (int32_t)idFb);

                pDpInfo->pDpWinRootVr->RegionsChanged(hFb);
            }

            CrFbUpdateEnd(hFb);
        }
        else
            WARN(("CrFbUpdateBegin failed %d", rc));
    }

    return VINF_SUCCESS;
}

/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */
int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap)
{
    CrFBmInit(pMap);
    for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled();
            hFb;
            hFb = CrPMgrFbGetNextEnabled(hFb))
    {
        int rc = CrFbUpdateBegin(hFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("UpdateBegin failed, rc %d", rc));
            for (HCR_FRAMEBUFFER hTmpFb = CrPMgrFbGetFirstEnabled();
                        hFb != hTmpFb;
                        hTmpFb = CrPMgrFbGetNextEnabled(hTmpFb))
            {
                CrFbUpdateEnd(hTmpFb);
                CrFBmClear(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex);
            }
            return rc;
        }

        CrFBmSet(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex);
    }

    return VINF_SUCCESS;
}

/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */
void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap)
{
    for (uint32_t i = 0; i < (uint32_t)cr_server.screenCount; ++i)
    {
        if (!CrFBmIsSet(pMap, i))
            continue;

        HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i);
        CRASSERT(hFb);
        CrFbUpdateEnd(hFb);
    }
}

int CrPMgrResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM, const uint32_t *pTargetMap)
{
    int rc = VINF_SUCCESS;

    if (pScreen->u32ViewIndex == 0xffffffff)
    {
        /* this is just a request to disable targets, search and disable */
        for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount);
                i >= 0;
                i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i))
        {
            CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i];
            if (pDpInfo->iFb < 0)
                continue;

            Assert(pDpInfo->iFb < cr_server.screenCount);
            HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb);

            rc = crPMgrFbDisconnectTarget(hAssignedFb, (uint32_t)i);
            if (RT_FAILURE(rc))
            {
                WARN(("crPMgrFbDisconnectTarget failed %d", rc));
                return rc;
            }
        }

        return VINF_SUCCESS;
    }

    HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pScreen->u32ViewIndex);
    if (!hFb)
    {
        WARN(("CrPMgrFbGet failed"));
        return VERR_INVALID_PARAMETER;
    }

    const VBVAINFOSCREEN *pFbScreen = CrFbGetScreenInfo(hFb);
    bool fFbInfoChanged = true;

    if (!memcmp(pFbScreen, pScreen, sizeof (*pScreen)))
    {
        if (!pvVRAM || pvVRAM == CrFbGetVRAM(hFb))
            fFbInfoChanged = false;
    }

    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[pScreen->u32ViewIndex];

    VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aRemovedTargetMap);
    VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aAddedTargetMap);

    bool fDisplaysAdded = false, fDisplaysRemoved = false;

    memcpy(aRemovedTargetMap, pFbInfo->aTargetMap, sizeof (aRemovedTargetMap));

    if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
    {
        /* so far there is no need in keeping displays attached to disabled Framebffer,
         * just disconnect everything */
        for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i)
        {
            if (aRemovedTargetMap[i])
            {
                fDisplaysRemoved = true;
                break;
            }
        }

        memset(aAddedTargetMap, 0, sizeof (aAddedTargetMap));
    }
    else
    {
        for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i)
        {
            aRemovedTargetMap[i] = (aRemovedTargetMap[i] & ~pTargetMap[i]);
            if (aRemovedTargetMap[i])
                fDisplaysRemoved = true;
        }

        memcpy(aAddedTargetMap, pFbInfo->aTargetMap, sizeof (aAddedTargetMap));
        for (int i = 0; i < RT_ELEMENTS(aAddedTargetMap); ++i)
        {
            aAddedTargetMap[i] = (pTargetMap[i] & ~aAddedTargetMap[i]);
            if (aAddedTargetMap[i])
                fDisplaysAdded = true;
        }
    }

    if (!fFbInfoChanged && !fDisplaysRemoved && !fDisplaysAdded)
    {
        crDebug("resize: no changes");
        return VINF_SUCCESS;
    }

    if (fDisplaysRemoved)
    {
        rc = crPMgrFbDisconnect(hFb, aRemovedTargetMap);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbDisconnect failed %d", rc));
            return rc;
        }
    }

    if (fFbInfoChanged)
    {
#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS
        rc = crPMgrModeModify(hFb, 0, CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR);
        if (!RT_SUCCESS(rc))
        {
            WARN(("crPMgrModeModifyTarget failed %d", rc));
            return rc;
        }
#endif
        rc = CrFbUpdateBegin(hFb);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrFbUpdateBegin failed %d", rc));
            return rc;
        }

        crVBoxServerMuralFbResizeBegin(hFb);

        rc = CrFbResize(hFb, pScreen, pvVRAM);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrFbResize failed %d", rc));
        }

        crVBoxServerMuralFbResizeEnd(hFb);

        CrFbUpdateEnd(hFb);
    }

    if (fDisplaysAdded)
    {
        rc = crPMgrFbConnect(hFb, aAddedTargetMap);
        if (RT_FAILURE(rc))
        {
            WARN(("crPMgrFbConnect failed %d", rc));
            return rc;
        }
    }

    return VINF_SUCCESS;
}

int CrFbEntrySaveState(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY *hEntry, PSSMHANDLE pSSM)
{
    const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry);
    CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
    CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
    int rc = SSMR3PutU32(pSSM, pFbTex->pTobj->id);
    AssertRCReturn(rc, rc);
    uint32_t u32 = 0;

    u32 = CrVrScrCompositorEntryFlagsGet(pEntry);
    rc = SSMR3PutU32(pSSM, u32);
    AssertRCReturn(rc, rc);

    const RTRECT *pRect = CrVrScrCompositorEntryRectGet(pEntry);

    rc = SSMR3PutS32(pSSM, pRect->xLeft);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutS32(pSSM, pRect->yTop);
    AssertRCReturn(rc, rc);
#if 0
    rc = SSMR3PutS32(pSSM, pRect->xRight);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutS32(pSSM, pRect->yBottom);
    AssertRCReturn(rc, rc);
#endif

    rc = CrVrScrCompositorEntryRegionsGet(&pFb->Compositor, pEntry, &u32, NULL, NULL, &pRect);
    AssertRCReturn(rc, rc);

    rc = SSMR3PutU32(pSSM, u32);
    AssertRCReturn(rc, rc);

    if (u32)
    {
        rc = SSMR3PutMem(pSSM, pRect, u32 * sizeof (*pRect));
        AssertRCReturn(rc, rc);
    }
    return rc;
}

int CrFbSaveState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM)
{
    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
    CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter);
    const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
    uint32_t u32 = 0;
    while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
    {
        CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
        CRASSERT(pTexData);
        CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
        if (pFbTex->pTobj)
            ++u32;
    }

    int rc = SSMR3PutU32(pSSM, u32);
    AssertRCReturn(rc, rc);

    CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter);

    while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
    {
        CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
        CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);
        if (pFbTex->pTobj)
        {
            HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
            rc = CrFbEntrySaveState(pFb, hEntry, pSSM);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

int CrPMgrSaveState(PSSMHANDLE pSSM)
{
    int rc;
    int cDisplays = 0, i;

    for (i = 0; i < cr_server.screenCount; ++i)
    {
        if (CrPMgrFbGetEnabled(i))
            ++cDisplays;
    }

    rc = SSMR3PutS32(pSSM, cDisplays);
    AssertRCReturn(rc, rc);

    if (!cDisplays)
        return VINF_SUCCESS;

    rc = SSMR3PutS32(pSSM, cr_server.screenCount);
    AssertRCReturn(rc, rc);

    for (i = 0; i < cr_server.screenCount; ++i)
    {
        CR_FRAMEBUFFER *hFb = CrPMgrFbGetEnabled(i);
        if (hFb)
        {
            Assert(hFb->ScreenInfo.u32ViewIndex == i);
            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32ViewIndex);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginX);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginY);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32LineSize);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Width);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Height);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16BitsPerPixel);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16Flags);
            AssertRCReturn(rc, rc);

            rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset);
            AssertRCReturn(rc, rc);

            CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[hFb->ScreenInfo.u32ViewIndex];
            rc = SSMR3PutMem(pSSM, pFbInfo->aTargetMap, sizeof (pFbInfo->aTargetMap));
            AssertRCReturn(rc, rc);

            rc = CrFbSaveState(hFb, pSSM);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

int CrFbEntryLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version)
{
    uint32_t texture;
    int  rc = SSMR3GetU32(pSSM, &texture);
    AssertRCReturn(rc, rc);

    uint32_t fFlags;
    rc = SSMR3GetU32(pSSM, &fFlags);
    AssertRCReturn(rc, rc);


    HCR_FRAMEBUFFER_ENTRY hEntry;

    rc = CrFbEntryCreateForTexId(pFb, texture, fFlags, &hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrFbEntryCreateForTexId Failed"));
        return rc;
    }

    Assert(hEntry);

    const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry);
    CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
    CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData);

    RTPOINT Point;
    rc = SSMR3GetS32(pSSM, &Point.x);
    AssertRCReturn(rc, rc);

    rc = SSMR3GetS32(pSSM, &Point.y);
    AssertRCReturn(rc, rc);

    uint32_t cRects;
    rc = SSMR3GetU32(pSSM, &cRects);
    AssertRCReturn(rc, rc);

    RTRECT * pRects = NULL;
    if (cRects)
    {
        pRects = (RTRECT *)crAlloc(cRects * sizeof (*pRects));
        AssertReturn(pRects, VERR_NO_MEMORY);

        rc = SSMR3GetMem(pSSM, pRects, cRects * sizeof (*pRects));
        AssertRCReturn(rc, rc);
    }

    rc = CrFbEntryRegionsSet(pFb, hEntry, &Point, cRects, pRects, false);
    AssertRCReturn(rc, rc);

    if (pRects)
        crFree(pRects);

    CrFbEntryRelease(pFb, hEntry);

    return VINF_SUCCESS;
}

int CrFbLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version)
{
    uint32_t u32 = 0;
    int rc = SSMR3GetU32(pSSM, &u32);
    AssertRCReturn(rc, rc);

    if (!u32)
        return VINF_SUCCESS;

    rc = CrFbUpdateBegin(pFb);
    AssertRCReturn(rc, rc);

    for (uint32_t i = 0; i < u32; ++i)
    {
        rc = CrFbEntryLoadState(pFb, pSSM, version);
        AssertRCReturn(rc, rc);
    }

    CrFbUpdateEnd(pFb);

    return VINF_SUCCESS;
}

int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version)
{
    int rc;
    int cDisplays, screenCount, i;

    rc = SSMR3GetS32(pSSM, &cDisplays);
    AssertRCReturn(rc, rc);

    if (!cDisplays)
        return VINF_SUCCESS;

    rc = SSMR3GetS32(pSSM, &screenCount);
    AssertRCReturn(rc, rc);

    CRASSERT(screenCount == cr_server.screenCount);

    CRScreenInfo screen[CR_MAX_GUEST_MONITORS];

    if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO)
    {
        for (i = 0; i < cr_server.screenCount; ++i)
        {
            rc = SSMR3GetS32(pSSM, &screen[i].x);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetS32(pSSM, &screen[i].y);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &screen[i].w);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &screen[i].h);
            AssertRCReturn(rc, rc);
        }
    }

    for (i = 0; i < cDisplays; ++i)
    {
        int iScreen;

        rc = SSMR3GetS32(pSSM, &iScreen);
        AssertRCReturn(rc, rc);

        CR_FRAMEBUFFER *pFb = CrPMgrFbGet(iScreen);
        Assert(pFb);

        VBVAINFOSCREEN Screen;

        Screen.u32ViewIndex = iScreen;

        VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);

        memset(aTargetMap, 0, sizeof (aTargetMap));
        ASMBitSet(aTargetMap, iScreen);

        if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO)
        {
            memset(&Screen, 0, sizeof (Screen));
            Screen.u32LineSize = 4 * screen[iScreen].w;
            Screen.u32Width = screen[iScreen].w;
            Screen.u32Height = screen[iScreen].h;
            Screen.u16BitsPerPixel = 4;
            Screen.u16Flags = VBVA_SCREEN_F_ACTIVE;
        }
        else
        {
            rc = SSMR3GetS32(pSSM, &Screen.i32OriginX);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetS32(pSSM, &Screen.i32OriginY);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &Screen.u32LineSize);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &Screen.u32Width);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &Screen.u32Height);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU16(pSSM, &Screen.u16BitsPerPixel);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU16(pSSM, &Screen.u16Flags);
            AssertRCReturn(rc, rc);

            rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset);
            AssertRCReturn(rc, rc);
            if (Screen.u32StartOffset == 0xffffffff)
            {
                WARN(("not expected offVram"));
                Screen.u32StartOffset = 0;
            }

            if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_MAP_REORDERED)
            {
                rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap));
                AssertRCReturn(rc, rc);
            }

            if (version == SHCROGL_SSM_VERSION_WITH_SCREEN_MAP)
            {
                VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aEmptyTargetMap);

                memset(aEmptyTargetMap, 0, sizeof (aEmptyTargetMap));

                rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aEmptyTargetMap);
                AssertRCReturn(rc, rc);

                rc = CrFbLoadState(pFb, pSSM, version);
                AssertRCReturn(rc, rc);

                rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap));
                AssertRCReturn(rc, rc);
            }
        }

        rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aTargetMap);
        AssertRCReturn(rc, rc);

        if (version >= SHCROGL_SSM_VERSION_WITH_FB_INFO && version != SHCROGL_SSM_VERSION_WITH_SCREEN_MAP)
        {
            rc = CrFbLoadState(pFb, pSSM, version);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}


void SERVER_DISPATCH_APIENTRY
crServerDispatchVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects)
{
    uint32_t idFb = CR_PRESENT_GET_SCREEN(cfg);
    if (idFb >= CR_MAX_GUEST_MONITORS)
    {
        WARN(("Invalid guest screen"));
        return;
    }

    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb);
    if (!hFb)
    {
        WARN(("request to present on disabled framebuffer, ignore"));
        return;
    }

    HCR_FRAMEBUFFER_ENTRY hEntry;
    int rc;
    if (texture)
    {
        rc = CrFbEntryCreateForTexId(hFb, texture, (cfg & CR_PRESENT_FLAG_TEX_NONINVERT_YCOORD) ? 0 : CRBLT_F_INVERT_SRC_YCOORDS, &hEntry);
        if (!RT_SUCCESS(rc))
        {
            LOG(("CrFbEntryCreateForTexId Failed"));
            return;
        }

        Assert(hEntry);

#if 0
        if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
        {
            CR_SERVER_DUMP_TEXPRESENT(&pEntry->CEntry.Tex);
        }
#endif
    }
    else
        hEntry = NULL;

    rc = CrFbUpdateBegin(hFb);
    if (RT_SUCCESS(rc))
    {
        if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
        {
            RTPOINT Point = {xPos, yPos};
            rc = CrFbEntryRegionsAdd(hFb, hEntry, &Point, (uint32_t)cRects, (const RTRECT*)pRects, false);
        }
        else
        {
            CrFbRegionsClear(hFb);
        }

        CrFbUpdateEnd(hFb);
    }
    else
    {
        WARN(("CrFbUpdateBegin Failed"));
    }

    if (hEntry)
        CrFbEntryRelease(hFb, hEntry);
}

DECLINLINE(void) crVBoxPRectUnpack(const VBOXCMDVBVA_RECT *pVbvaRect, RTRECT *pRect)
{
    pRect->xLeft = pVbvaRect->xLeft;
    pRect->yTop = pVbvaRect->yTop;
    pRect->xRight = pVbvaRect->xRight;
    pRect->yBottom = pVbvaRect->yBottom;
}

DECLINLINE(void) crVBoxPRectUnpacks(const VBOXCMDVBVA_RECT *paVbvaRects, RTRECT *paRects, uint32_t cRects)
{
    uint32_t i = 0;
    for (; i < cRects; ++i)
    {
        crVBoxPRectUnpack(&paVbvaRects[i], &paRects[i]);
    }
}

static RTRECT * crVBoxServerCrCmdBltRecsUnpack(const VBOXCMDVBVA_RECT *pPRects, uint32_t cRects)
{
    if (g_CrPresenter.cbTmpBuf < cRects * sizeof (RTRECT))
    {
        if (g_CrPresenter.pvTmpBuf)
            RTMemFree(g_CrPresenter.pvTmpBuf);

        g_CrPresenter.cbTmpBuf = (cRects + 10) * sizeof (RTRECT);
        g_CrPresenter.pvTmpBuf = RTMemAlloc(g_CrPresenter.cbTmpBuf);
        if (!g_CrPresenter.pvTmpBuf)
        {
            WARN(("RTMemAlloc failed!"));
            g_CrPresenter.cbTmpBuf = 0;
            return NULL;
        }
    }

    RTRECT *pRects = (RTRECT *)g_CrPresenter.pvTmpBuf;
    crVBoxPRectUnpacks(pPRects, pRects, cRects);

    return pRects;
}

static void crPMgrPrimaryUpdateScreen(HCR_FRAMEBUFFER hFb, uint32_t idScreen, uint32_t cRects, const RTRECT *pRects)
{
    const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);

    bool fDirtyEmpty = true;
    RTRECT dirtyRect = {0};
    cr_server.CrCmdClientInfo.pfnCltScrUpdateBegin(cr_server.CrCmdClientInfo.hCltScr, idScreen);

    VBVACMDHDR hdr;
    for (uint32_t i = 0; i < cRects; ++i)
    {
        hdr.x = pRects[i].xLeft;
        hdr.y = pRects[i].yTop;
        hdr.w = hdr.x + pRects[i].xRight;
        hdr.h = hdr.y + pRects[i].yBottom;

        cr_server.CrCmdClientInfo.pfnCltScrUpdateProcess(cr_server.CrCmdClientInfo.hCltScr, idScreen, &hdr, sizeof (hdr));

        if (fDirtyEmpty)
        {
            /* This is the first rectangle to be added. */
            dirtyRect.xLeft   = pRects[i].xLeft;
            dirtyRect.yTop    = pRects[i].yTop;
            dirtyRect.xRight  = pRects[i].xRight;
            dirtyRect.yBottom = pRects[i].yBottom;
            fDirtyEmpty       = false;
        }
        else
        {
            /* Adjust region coordinates. */
            if (dirtyRect.xLeft > pRects[i].xLeft)
            {
                dirtyRect.xLeft = pRects[i].xLeft;
            }

            if (dirtyRect.yTop > pRects[i].yTop)
            {
                dirtyRect.yTop = pRects[i].yTop;
            }

            if (dirtyRect.xRight < pRects[i].xRight)
            {
                dirtyRect.xRight = pRects[i].xRight;
            }

            if (dirtyRect.yBottom < pRects[i].yBottom)
            {
                dirtyRect.yBottom = pRects[i].yBottom;
            }
        }
    }

    if (dirtyRect.xRight - dirtyRect.xLeft)
    {
        cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, pScreen->i32OriginX + dirtyRect.xLeft, pScreen->i32OriginY + dirtyRect.yTop,
                                           dirtyRect.xRight - dirtyRect.xLeft, dirtyRect.yBottom - dirtyRect.yTop);
    }
    else
    {
        cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, 0, 0, 0, 0);
    }

}

static void crPMgrPrimaryUpdate(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects)
{
    if (!cRects)
        return;

    const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);

    uint32_t idFb = pScreen->u32ViewIndex;
    CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb];

    for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount);
            i >= 0;
            i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i))
    {
        crPMgrPrimaryUpdateScreen(hFb, i, cRects, pRects);
    }
}

static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary)
{
    CR_BLITTER_IMG Img;
    int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
    if (i8Result)
    {
        WARN(("invalid param"));
        return -1;
    }

    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
    if (!hFb)
    {
        WARN(("request to present on disabled framebuffer"));
        return -1;
    }

    if (!fToPrimary)
    {
        int rc = CrFbBltGetContents(hFb, pPos, cRects, pRects, &Img);
        if (!RT_SUCCESS(rc))
        {
            WARN(("CrFbBltGetContents failed %d", rc));
            return -1;
        }

        return 0;
    }

    int rc = CrFbBltPutContentsNe(hFb, pPos, cRects, pRects, &Img);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrFbBltPutContentsNe failed %d", rc));
        return -1;
    }

    return 0;
}

static int8_t crVBoxServerCrCmdBltPrimaryProcess(const VBOXCMDVBVA_BLT_PRIMARY *pCmd, uint32_t cbCmd)
{
    uint32_t u32PrimaryID = (uint32_t)pCmd->Hdr.Hdr.u.u8PrimaryID;
    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
    if (!hFb)
    {
        WARN(("request to present on disabled framebuffer, ignore"));
        return 0;
    }

    uint32_t cRects;
    const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
    if ((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) % sizeof (VBOXCMDVBVA_RECT))
    {
        WARN(("invalid argument size"));
        return -1;
    }

    cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) / sizeof (VBOXCMDVBVA_RECT);

    RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
    if (!pRects)
    {
        WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
        return -1;
    }

    uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;

    if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
    {
        uint32_t texId = pCmd->alloc.u.id;
        if (!texId)
        {
            WARN(("texId is NULL!\n"));
            return -1;
        }

        if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
        {
            WARN(("blit from primary to texture not implemented"));
            return -1;
        }

        crServerDispatchVBoxTexPresent(texId, u32PrimaryID, pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y, cRects, (const GLint*)pRects);

        return 0;
    }
    else
    {
        const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
        uint32_t width = pScreen->u32Width, height = pScreen->u32Height;
        VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM;

        bool fToPrymary = !(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2);
        RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
        int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(u32PrimaryID, offVRAM, width, height, &Pos, cRects, pRects, fToPrymary);
        if (i8Result < 0)
        {
            WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed"));
            return i8Result;
        }

        if (!fToPrymary)
            return 0;
    }

    crPMgrPrimaryUpdate(hFb, cRects, pRects);

    return 0;
}

static int8_t crVBoxServerCrCmdBltIdToVramMem(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
{
    CR_TEXDATA* pTex = CrFbTexDataAcquire(hostId);
    if (!pTex)
    {
        WARN(("pTex failed for %d", hostId));
        return -1;
    }

    const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex);
    if (!width)
    {
        width = pVrTex->width;
        height = pVrTex->height;
    }

    CR_BLITTER_IMG Img;
    int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
    if (i8Result)
    {
        WARN(("invalid param"));
        return -1;
    }

    int rc = CrTdBltEnter(pTex);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrTdBltEnter failed %d", rc));
        return -1;
    }

    rc = crFbTexDataGetContents(pTex, pPos, cRects, pRects, &Img);

    CrTdBltLeave(pTex);

    CrTdRelease(pTex);

    if (!RT_SUCCESS(rc))
    {
        WARN(("crFbTexDataGetContents failed %d", rc));
        return -1;
    }

    return 0;
}

static int8_t crVBoxServerCrCmdBltIdToVram(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
{
    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledByVramStart(offVRAM);
    if (hFb)
    {
        const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb);
        Assert(!width || pScreen->u32Width == width);
        Assert(!height || pScreen->u32Height == height);

        crServerDispatchVBoxTexPresent(hostId, pScreen->u32ViewIndex, pPos->x, pPos->y, cRects, (const GLint*)pRects);
        return 0;
    }

    return crVBoxServerCrCmdBltIdToVramMem(hostId, offVRAM, width, height, pPos, cRects, pRects);
}

static int8_t crVBoxServerCrCmdBltVramToVramMem(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight, VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
{
    CR_BLITTER_IMG srcImg, dstImg;
    int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &srcImg);
    if (i8Result)
    {
        WARN(("invalid param"));
        return -1;
    }

    i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &dstImg);
    if (i8Result)
    {
        WARN(("invalid param"));
        return -1;
    }

    CrMBltImg(&srcImg, pPos, cRects, pRects, &dstImg);

    return 0;
}

static int8_t crVBoxServerCrCmdBltVramToVram(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight,
        VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight,
        const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects)
{
    HCR_FRAMEBUFFER hSrcFb = CrPMgrFbGetEnabledByVramStart(offSrcVRAM);
    HCR_FRAMEBUFFER hDstFb = CrPMgrFbGetEnabledByVramStart(offDstVRAM);

    if (hDstFb)
    {
        if (hSrcFb)
        {
            LOG(("blit from one framebuffer, wow"));

            int rc = CrFbUpdateBegin(hSrcFb);
            if (RT_SUCCESS(rc))
            {
                CrFbRegionsClear(hSrcFb);

                CrFbUpdateEnd(hSrcFb);
            }
            else
                WARN(("CrFbUpdateBegin failed %d", rc));
        }

        CR_BLITTER_IMG Img;
        int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &Img);
        if (i8Result)
        {
            WARN(("invalid param"));
            return -1;
        }

        const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hDstFb);
        if (pScreen->u32Width == dstWidth && pScreen->u32Height == dstHeight)
        {
            int rc = CrFbBltPutContentsNe(hDstFb, pPos, cRects, pRects, &Img);
            if (RT_FAILURE(rc))
            {
                WARN(("CrFbBltPutContentsNe failed %d", rc));
                return -1;
            }
        }
        else
        {
            int rc = CrFbUpdateBegin(hDstFb);
            if (RT_SUCCESS(rc))
            {
                CrFbRegionsClear(hDstFb);

                CrFbUpdateEnd(hDstFb);
            }
            else
                WARN(("CrFbUpdateBegin failed %d", rc));

            rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
            if (RT_FAILURE(rc))
            {
                WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc));
                return -1;
            }
        }

        crPMgrPrimaryUpdate(hDstFb, cRects, pRects);

        return 0;
    }
    else if (hSrcFb)
    {
        CR_BLITTER_IMG Img;
        int8_t i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &Img);
        if (i8Result)
        {
            WARN(("invalid param"));
            return -1;
        }

        const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hSrcFb);
        if (pScreen->u32Width == srcWidth && pScreen->u32Height == srcHeight)
        {
            int rc = CrFbBltGetContents(hSrcFb, pPos, cRects, pRects, &Img);
            if (RT_FAILURE(rc))
            {
                WARN(("CrFbBltGetContents failed %d", rc));
                return -1;
            }
        }
        else
        {
            int rc = CrFbUpdateBegin(hSrcFb);
            if (RT_SUCCESS(rc))
            {
                CrFbRegionsClear(hSrcFb);

                CrFbUpdateEnd(hSrcFb);
            }
            else
                WARN(("CrFbUpdateBegin failed %d", rc));

            rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
            if (RT_FAILURE(rc))
            {
                WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc));
                return -1;
            }
        }

        return 0;
    }

    return crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects);
}


static int8_t crVBoxServerCrCmdBltOffIdProcess(const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *pCmd, uint32_t cbCmd)
{
    uint32_t cRects;
    const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
    if ((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) % sizeof (VBOXCMDVBVA_RECT))
    {
        WARN(("invalid argument size"));
        return -1;
    }

    cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) / sizeof (VBOXCMDVBVA_RECT);

    RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
    if (!pRects)
    {
        WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
        return -1;
    }

    uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
    uint32_t hostId = pCmd->id;

    Assert(u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID);

    if (!hostId)
    {
        WARN(("zero host id"));
        return -1;
    }

    if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
    {
        WARN(("blit from texture to texture not implemented"));
        return -1;
    }

    if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
    {
        WARN(("blit to texture not implemented"));
        return -1;
    }

    VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM;

    RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
    return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, 0, 0, &Pos, cRects, pRects);
}

static int8_t crVBoxServerCrCmdBltSameDimOrId(const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *pCmd, uint32_t cbCmd)
{
    uint32_t cRects;
    const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
    if ((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
    {
        WARN(("invalid argument size"));
        return -1;
    }

    cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);

    RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
    if (!pRects)
    {
        WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
        return -1;
    }

    uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
    VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc1.Info.u.offVRAM;
    uint32_t width = pCmd->alloc1.u16Width;
    uint32_t height = pCmd->alloc1.u16Height;
    RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};

    if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
    {
        uint32_t hostId = pCmd->info2.u.id;

        if (!hostId)
        {
            WARN(("zero host id"));
            return -1;
        }

        if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
        {
            WARN(("blit from texture to texture not implemented"));
            return -1;
        }

        if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
        {
            WARN(("blit to texture not implemented"));
            return -1;
        }

        return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, width, height, &Pos, cRects, pRects);
    }

    if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
    {
        if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2))
        {
            WARN(("blit to texture not implemented"));
            return -1;
        }

        return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects);
    }

    if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
        crVBoxServerCrCmdBltVramToVram(offVRAM, width, height, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects);
    else
        crVBoxServerCrCmdBltVramToVram(pCmd->info2.u.offVRAM, width, height, offVRAM, width, height, &Pos, cRects, pRects);

    return 0;
}

static int8_t crVBoxServerCrCmdBltGenericBGRAProcess(const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd)
{
    uint32_t cRects;
    const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
    if ((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
    {
        WARN(("invalid argument size"));
        return -1;
    }

    cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);

    RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
    if (!pRects)
    {
        WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
        return -1;
    }

    uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
    RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};

    if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID)
    {
        if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
        {
            WARN(("blit from texture to texture not implemented"));
            return -1;
        }

        if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
        {
            WARN(("blit to texture not implemented"));
            return -1;
        }

        return crVBoxServerCrCmdBltIdToVram(pCmd->alloc2.Info.u.id, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects);
    }
    else
    {
        if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
        {
            if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2))
            {
                WARN(("blit to texture not implemented"));
                return -1;
            }

            RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y};
            return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects);
        }

        if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)
            crVBoxServerCrCmdBltVramToVram(pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects);
        else
            crVBoxServerCrCmdBltVramToVram(pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects);

        return 0;
    }
}

static int8_t crVBoxServerCrCmdClrFillPrimaryGenericProcess(uint32_t u32PrimaryID, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color)
{
    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID);
    if (!hFb)
    {
        WARN(("request to present on disabled framebuffer, ignore"));
        return 0;
    }

    int rc = CrFbClrFillNe(hFb, cRects, pRects, u32Color);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrFbClrFillNe failed %d", rc));
        return -1;
    }

    return 0;
}

static int8_t crVBoxServerCrCmdClrFillVramGenericProcess(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color)
{
    CR_BLITTER_IMG Img;
    int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img);
    if (i8Result)
    {
        WARN(("invalid param"));
        return -1;
    }

    CrMClrFillImg(&Img, cRects, pRects, u32Color);

    return 0;
}

static int8_t crVBoxServerCrCmdClrFillGenericBGRAProcess(const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd)
{
    uint32_t cRects;
    const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects;
    if ((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT))
    {
        WARN(("invalid argument size"));
        return -1;
    }

    cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT);

    RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects);
    if (!pRects)
    {
        WARN(("crVBoxServerCrCmdBltRecsUnpack failed"));
        return -1;
    }

//    uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags;
    int8_t i8Result = crVBoxServerCrCmdClrFillVramGenericProcess(pCmd->dst.Info.u.offVRAM, pCmd->dst.u16Width, pCmd->dst.u16Height, pRects, cRects, pCmd->Hdr.u32Color);
    if (i8Result < 0)
    {
        WARN(("crVBoxServerCrCmdClrFillVramGenericProcess failed"));
        return i8Result;
    }

    return 0;
}

int8_t crVBoxServerCrCmdClrFillProcess(const VBOXCMDVBVA_CLRFILL_HDR *pCmd, uint32_t cbCmd)
{
    uint8_t u8Flags = pCmd->Hdr.u8Flags;
    uint8_t u8Cmd = (VBOXCMDVBVA_OPF_CLRFILL_TYPE_MASK & u8Flags);

    switch (u8Cmd)
    {
        case VBOXCMDVBVA_OPF_CLRFILL_TYPE_GENERIC_A8R8G8B8:
        {
            if (cbCmd < sizeof (VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8))
            {
                WARN(("VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8: invalid command size"));
                return -1;
            }

            return crVBoxServerCrCmdClrFillGenericBGRAProcess((const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8*)pCmd, cbCmd);
        }
        default:
            WARN(("unsupported command"));
            return -1;
    }

}

int8_t crVBoxServerCrCmdBltProcess(const VBOXCMDVBVA_BLT_HDR *pCmd, uint32_t cbCmd)
{
    uint8_t u8Flags = pCmd->Hdr.u8Flags;
    uint8_t u8Cmd = (VBOXCMDVBVA_OPF_BLT_TYPE_MASK & u8Flags);

    switch (u8Cmd)
    {
        case VBOXCMDVBVA_OPF_BLT_TYPE_SAMEDIM_A8R8G8B8:
        {
            if (cbCmd < sizeof (VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8))
            {
                WARN(("VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8: invalid command size"));
                return -1;
            }

            return crVBoxServerCrCmdBltSameDimOrId((const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *)pCmd, cbCmd);
        }
        case VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID:
        {
            if (cbCmd < sizeof (VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID))
            {
                WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID: invalid command size"));
                return -1;
            }

            return crVBoxServerCrCmdBltOffIdProcess((const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *)pCmd, cbCmd);
        }
        case VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8:
        {
            if (cbCmd < sizeof (VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8))
            {
                WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8: invalid command size"));
                return -1;
            }

            return crVBoxServerCrCmdBltGenericBGRAProcess((const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *)pCmd, cbCmd);
        }
        default:
            WARN(("unsupported command"));
            return -1;
    }
}

int8_t crVBoxServerCrCmdFlipProcess(const VBOXCMDVBVA_FLIP *pFlip)
{
    uint32_t hostId;
    if (pFlip->Hdr.u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID)
    {
        hostId = pFlip->src.u.id;
        if (!hostId)
        {
            WARN(("hostId is NULL"));
            return -1;
        }
    }
    else
    {
        WARN(("VBOXCMDVBVA_OPF_ALLOC_SRCID not specified"));
        hostId = 0;
    }

    uint32_t idFb = pFlip->Hdr.u.u8PrimaryID;
    HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb);
    if (!hFb)
    {
        WARN(("request to present on disabled framebuffer, ignore"));
        return 0;
    }

    const RTRECT *pRect = CrVrScrCompositorRectGet(&hFb->Compositor);
    crServerDispatchVBoxTexPresent(hostId, idFb, 0, 0, 1, (const GLint*)pRect);
    return 0;
}

typedef struct CRSERVER_CLIENT_CALLOUT
{
    VBOXCRCMDCTL_CALLOUT_LISTENTRY Entry;
    PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb;
    void*pvCb;
} CRSERVER_CLIENT_CALLOUT;

static DECLCALLBACK(void) crServerClientCalloutCb(struct VBOXCRCMDCTL_CALLOUT_LISTENTRY *pEntry)
{
    CRSERVER_CLIENT_CALLOUT *pCallout = RT_FROM_MEMBER(pEntry, CRSERVER_CLIENT_CALLOUT, Entry);
    pCallout->pfnCb(pCallout->pvCb);
    int rc = RTSemEventSignal(cr_server.hCalloutCompletionEvent);
    if (RT_FAILURE(rc))
        WARN(("RTSemEventSignal failed rc %d", rc));
}

static DECLCALLBACK(void) crServerClientCallout(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb)
{
    Assert(cr_server.pCurrentCalloutCtl);
    CRSERVER_CLIENT_CALLOUT Callout;
    Callout.pfnCb = pfnCb;
    Callout.pvCb = pvCb;
    cr_server.ClientInfo.pfnCallout(cr_server.ClientInfo.hClient, cr_server.pCurrentCalloutCtl, &Callout.Entry, crServerClientCalloutCb);

    int rc = RTSemEventWait(cr_server.hCalloutCompletionEvent, RT_INDEFINITE_WAIT);
    if (RT_FAILURE(rc))
        WARN(("RTSemEventWait failed %d", rc));
}


DECLEXPORT(void) crVBoxServerCalloutEnable(VBOXCRCMDCTL *pCtl)
{
#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS
    Assert(!cr_server.pCurrentCalloutCtl);
    cr_server.pCurrentCalloutCtl = pCtl;

    cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, (void*)crServerClientCallout);
#endif
}

extern DECLEXPORT(void) crVBoxServerCalloutDisable()
{
#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS
    Assert(cr_server.pCurrentCalloutCtl);

    cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, NULL);

    cr_server.pCurrentCalloutCtl = NULL;
#endif
}
