/* $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 <iprt/cdefs.h>
#include <iprt/types.h>
#include <iprt/asm.h>
#include <iprt/mem.h>
#include <iprt/list.h>
#include <iprt/memcache.h>

#include "render/renderspu.h"

/* DISPLAY */

int CrDpInit(PCR_DISPLAY pDisplay)
{
    const GLint visBits = cr_server.MainContextInfo.CreateInfo.visualBits;
    if (crServerMuralInit(&pDisplay->Mural, "", visBits, -1, GL_FALSE) < 0)
    {
        crWarning("crServerMuralInit failed!");
        return VERR_GENERAL_FAILURE;
    }

    crServerWindowVisibleRegion(&pDisplay->Mural);
    crServerDEntryAllVibleRegions(&pDisplay->Mural);

    crServerMuralShow(&pDisplay->Mural, GL_TRUE);

    pDisplay->fForcePresent = GL_FALSE;
    return VINF_SUCCESS;
}

void CrDpTerm(PCR_DISPLAY pDisplay)
{
    crServerMuralTerm(&pDisplay->Mural);
}

void CrDpResize(PCR_DISPLAY pDisplay, int32_t xPos, int32_t yPos, uint32_t width, uint32_t height)
{
    if (xPos != pDisplay->Mural.gX
            || yPos != pDisplay->Mural.gY
            || width != pDisplay->Mural.width
            || height != pDisplay->Mural.height)
    {
        crServerMuralPosition(&pDisplay->Mural, xPos, yPos, GL_TRUE);
        if (!crServerMuralSize(&pDisplay->Mural, width, height))
            crServerCheckMuralGeometry(&pDisplay->Mural);
    }
    else
        crServerCheckMuralGeometry(&pDisplay->Mural);
}

void CrDpReparent(PCR_DISPLAY pDisplay, CRScreenInfo *pScreen)
{
    renderspuSetWindowId(pScreen->winID);
    crServerWindowReparent(&pDisplay->Mural);
    renderspuSetWindowId(cr_server.screen[0].winID);

    CrDpResize(pDisplay, pScreen->x, pScreen->y, pScreen->w, pScreen->h);

    if (pScreen->winID)
    {
        /* need to do this on win, since otherwise the window ends up being with empty visible regions for some reason */
        crServerWindowVisibleRegion(&pDisplay->Mural);
    }
}


int CrDpSaveState(PCR_DISPLAY pDisplay, PSSMHANDLE pSSM)
{
    VBOXVR_SCR_COMPOSITOR_ITERATOR Iter;
    CrVrScrCompositorIterInit(&pDisplay->Mural.Compositor, &Iter);
    PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
    uint32_t u32 = 0;
    while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL)
    {
        ++u32;
    }

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

    CrVrScrCompositorIterInit(&pDisplay->Mural.Compositor, &Iter);

    while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL)
    {
        CR_DISPLAY_ENTRY *pDEntry = CR_DENTRY_FROM_CENTRY(pEntry);
        rc = CrDemEntrySaveState(pDEntry, pSSM);
        AssertRCReturn(rc, rc);

        u32 = CrVrScrCompositorEntryFlagsGet(&pDEntry->CEntry);
        rc = SSMR3PutU32(pSSM, u32);
        AssertRCReturn(rc, rc);

        rc = SSMR3PutS32(pSSM, CrVrScrCompositorEntryPosGet(&pDEntry->CEntry)->x);
        AssertRCReturn(rc, rc);

        rc = SSMR3PutS32(pSSM, CrVrScrCompositorEntryPosGet(&pDEntry->CEntry)->y);
        AssertRCReturn(rc, rc);

        const RTRECT * pRects;
        rc = CrVrScrCompositorEntryRegionsGet(&pDisplay->Mural.Compositor, &pDEntry->CEntry, &u32, NULL, NULL, &pRects);
        AssertRCReturn(rc, rc);

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

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

    return VINF_SUCCESS;
}

int CrDpLoadState(PCR_DISPLAY pDisplay, PSSMHANDLE pSSM, uint32_t version)
{
    uint32_t u32 = 0;
    int rc = SSMR3GetU32(pSSM, &u32);
    AssertRCReturn(rc, rc);

    if (!u32)
        return VINF_SUCCESS;

    CrDpEnter(pDisplay);

    for (uint32_t i = 0; i < u32; ++i)
    {
        CR_DISPLAY_ENTRY *pDEntry;
        rc = CrDemEntryLoadState(&cr_server.PresentTexturepMap, &pDEntry, pSSM);
        AssertRCReturn(rc, rc);

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

        CrVrScrCompositorEntryFlagsSet(&pDEntry->CEntry, fFlags);

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

        rc = SSMR3GetS32(pSSM, &Pos.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 = CrDpEntryRegionsAdd(pDisplay, pDEntry, &Pos, (uint32_t)cRects, (const RTRECT*)pRects, NULL);
        AssertRCReturn(rc, rc);

        if (pRects)
            crFree(pRects);
    }

    CrDpLeave(pDisplay);

    return VINF_SUCCESS;
}


int CrDpEntryRegionsSet(PCR_DISPLAY pDisplay, PCR_DISPLAY_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions)
{
    int rc = CrVrScrCompositorEntryRegionsSet(&pDisplay->Mural.Compositor, pEntry ? &pEntry->CEntry : NULL, pPos, cRegions, paRegions, false, NULL);
    return rc;
}

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);
}

int CrDpEntryRegionsAdd(PCR_DISPLAY pDisplay, PCR_DISPLAY_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, CR_DISPLAY_ENTRY_MAP *pMap)
{
    uint32_t fChangeFlags = 0;
    VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL;

    if (pMap)
        CrDemEnter(pMap);

    int rc = CrVrScrCompositorEntryRegionsAdd(&pDisplay->Mural.Compositor, pEntry ? &pEntry->CEntry : NULL, pPos, cRegions, paRegions, false, &pReplacedScrEntry, &fChangeFlags);
    if (RT_SUCCESS(rc))
    {
        if (fChangeFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED)
        {
            bool fChanged = true;
            if (pDisplay->Mural.fRootVrOn)
            {
                int rc = crServerMuralSynchRootVr(&pDisplay->Mural, &fChanged);
                if (!RT_SUCCESS(rc))
                {
                    crWarning("crServerMuralSynchRootVr failed, rc %d", rc);
                    fChanged = false;
                }
            }

            if (fChanged)
                crServerWindowVisibleRegion(&pDisplay->Mural);

            crServerDEntryAllVibleRegions(&pDisplay->Mural);

            Assert(!pReplacedScrEntry);
        }
        else if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED)
        {
            if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED)
            {
                Assert(pReplacedScrEntry);
                Assert(pEntry);
                if (pDisplay->Mural.fRootVrOn)
                {
                    CR_DISPLAY_ENTRY *pReplacedDEntry = CR_DENTRY_FROM_CENTRY(pReplacedScrEntry);
                    Assert(CrVrScrCompositorEntryIsUsed(&pReplacedDEntry->RootVrCEntry));
                    Assert(!CrVrScrCompositorEntryIsUsed(&pEntry->RootVrCEntry));
                    CrVrScrCompositorEntryInit(&pEntry->RootVrCEntry, CrVrScrCompositorEntryTexGet(&pEntry->CEntry), NULL);
                    CrVrScrCompositorEntryFlagsSet(&pEntry->RootVrCEntry, CrVrScrCompositorEntryFlagsGet(&pEntry->CEntry));
                    CrVrScrCompositorEntryReplace(&pDisplay->Mural.RootVrCompositor, &pReplacedDEntry->RootVrCEntry, &pEntry->RootVrCEntry);
                }
            }
            else
            {
                Assert(!pReplacedScrEntry);
                if (pDisplay->Mural.fRootVrOn)
                {
                    bool fChanged = false;
                    int rc = crServerMuralSynchRootVr(&pDisplay->Mural, &fChanged);
                    if (RT_SUCCESS(rc))
                    {
                        if (fChanged)
                            crServerWindowVisibleRegion(&pDisplay->Mural);
                    }
                    else
                        crWarning("crServerMuralSynchRootVr failed, rc %d", rc);
                }
            }
        }
        else
        {
            Assert(!(fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED));
            Assert(!pReplacedScrEntry);
        }
    }
    else
        crWarning("CrVrScrCompositorEntryRegionsAdd failed, rc %d", rc);

    if (pMap)
        CrDemLeave(pMap, CR_DENTRY_FROM_CENTRY(pEntry), CR_DENTRY_FROM_CENTRY(pReplacedScrEntry));

    return rc;
}

void CrDpRegionsClear(PCR_DISPLAY pDisplay)
{
    bool fChanged = false;
    CrVrScrCompositorRegionsClear(&pDisplay->Mural.Compositor, &fChanged);
    if (fChanged)
    {
        crServerMuralVisibleRegion(&pDisplay->Mural, 0, NULL);
    }
}

#define PCR_DISPLAY_ENTRY_FROM_CENTRY(_pe) ((PCR_DISPLAY_ENTRY)((uint8_t*)(_pe) - RT_OFFSETOF(CR_DISPLAY_ENTRY, CEntry)))
static DECLCALLBACK(void) crDpEntryCEntryReleaseCB(const struct VBOXVR_SCR_COMPOSITOR *pCompositor, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacingEntry)
{
    PCR_DISPLAY_ENTRY pCEntry = PCR_DISPLAY_ENTRY_FROM_CENTRY(pEntry);
    CrDemEntryRelease(pCEntry);
}

void CrDpEntryInit(PCR_DISPLAY_ENTRY pEntry, const VBOXVR_TEXTURE *pTextureData, uint32_t fFlags, PFNVBOXVRSCRCOMPOSITOR_ENTRY_RELEASED pfnEntryReleased)
{
    CrVrScrCompositorEntryInit(&pEntry->CEntry, pTextureData, pfnEntryReleased);
    CrVrScrCompositorEntryFlagsSet(&pEntry->CEntry, fFlags);
    CrVrScrCompositorEntryInit(&pEntry->RootVrCEntry, pTextureData, NULL);
    CrVrScrCompositorEntryFlagsSet(&pEntry->RootVrCEntry, fFlags);
    pEntry->pvORInstance = NULL;
    pEntry->idPBO = 0;
    pEntry->idInvertTex = 0;
}

void CrDpEntryCleanup(PCR_DISPLAY_ENTRY pDEntry)
{
    if (pDEntry->idPBO)
    {
        CRASSERT(cr_server.bUsePBOForReadback);
        cr_server.head_spu->dispatch_table.DeleteBuffersARB(1, &pDEntry->idPBO);
        pDEntry->idPBO = 0;
    }

    if (pDEntry->idInvertTex)
    {
        cr_server.head_spu->dispatch_table.DeleteTextures(1, &pDEntry->idInvertTex);
        pDEntry->idInvertTex = 0;
    }

    if (pDEntry->pvORInstance)
    {
        cr_server.outputRedirect.CROREnd(pDEntry->pvORInstance);
        pDEntry->pvORInstance = NULL;
    }
}

void CrDpEnter(PCR_DISPLAY pDisplay)
{
    pDisplay->fForcePresent |= crServerVBoxCompositionPresentNeeded(&pDisplay->Mural);
    crServerVBoxCompositionDisableEnter(&pDisplay->Mural);
}

void CrDpLeave(PCR_DISPLAY pDisplay)
{
    pDisplay->Mural.fDataPresented = GL_TRUE;
    pDisplay->Mural.fOrPresentOnReenable = GL_TRUE;
    crServerVBoxCompositionDisableLeave(&pDisplay->Mural, pDisplay->fForcePresent);
    pDisplay->fForcePresent = GL_FALSE;
}

void CrDpRootUpdate(PCR_DISPLAY pDisplay)
{
    crVBoxServerUpdateMuralRootVisibleRegion(&pDisplay->Mural);
}


typedef struct CR_DEM_ENTRY_INFO
{
    CRTextureObj *pTobj;
    uint32_t cEntries;
} CR_DEM_ENTRY_INFO;

typedef struct CR_DEM_ENTRY
{
    CR_DISPLAY_ENTRY Entry;
    CR_DEM_ENTRY_INFO *pInfo;
    CR_DISPLAY_ENTRY_MAP *pMap;
    RTLISTNODE Node;
} CR_DEM_ENTRY;

#define PCR_DEM_ENTRY_FROM_ENTRY(_pEntry) ((CR_DEM_ENTRY*)((uint8_t*)(_pEntry) - RT_OFFSETOF(CR_DEM_ENTRY, Entry)))

static RTMEMCACHE g_VBoxCrDemLookasideList;
static RTMEMCACHE g_VBoxCrDemInfoLookasideList;

int CrDemGlobalInit()
{
    int rc = RTMemCacheCreate(&g_VBoxCrDemLookasideList, sizeof (CR_DEM_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_VBoxCrDemInfoLookasideList, sizeof (CR_DEM_ENTRY_INFO),
                                    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))
            return VINF_SUCCESS;
        else
            crWarning("RTMemCacheCreate failed rc %d", rc);

        RTMemCacheDestroy(g_VBoxCrDemLookasideList);
    }
    else
        crWarning("RTMemCacheCreate failed rc %d", rc);
    return VINF_SUCCESS;
}

void CrDemTeGlobalTerm()
{
    RTMemCacheDestroy(g_VBoxCrDemLookasideList);
    RTMemCacheDestroy(g_VBoxCrDemInfoLookasideList);
}

static CR_DEM_ENTRY* crDemEntryAlloc()
{
    return (CR_DEM_ENTRY*)RTMemCacheAlloc(g_VBoxCrDemLookasideList);
}

static CR_DEM_ENTRY_INFO* crDemEntryInfoAlloc()
{
    return (CR_DEM_ENTRY_INFO*)RTMemCacheAlloc(g_VBoxCrDemInfoLookasideList);
}

static void crDemEntryFree(CR_DEM_ENTRY* pDemEntry)
{
    CrDpEntryCleanup(&pDemEntry->Entry);
    RTMemCacheFree(g_VBoxCrDemLookasideList, pDemEntry);
}

static void crDemEntryInfoFree(CR_DEM_ENTRY_INFO* pDemEntryInfo)
{
    RTMemCacheFree(g_VBoxCrDemInfoLookasideList, pDemEntryInfo);
}

void crDemEntryRelease(PCR_DISPLAY_ENTRY_MAP pMap, CR_DEM_ENTRY *pDemEntry)
{
    CR_DEM_ENTRY_INFO *pInfo = pDemEntry->pInfo;
    CRTextureObj *pTobj = pInfo->pTobj;

    --pInfo->cEntries;

    if (!pInfo->cEntries)
    {
        CR_STATE_SHAREDOBJ_USAGE_CLEAR(pInfo->pTobj, cr_server.MainContextInfo.pContext);

        crHashtableDelete(pMap->pTexIdToDemInfoMap, pTobj->id, NULL);

        crDemEntryInfoFree(pInfo);
    }

    if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pTobj))
    {
        CRSharedState *pShared = crStateGlobalSharedAcquire();

        CRASSERT(pShared);
        /* on the host side, we need to delete an ogl texture object here as well, which crStateDeleteTextureCallback will do
         * in addition to calling crStateDeleteTextureObject to delete a state object */
        crHashtableDelete(pShared->textureTable, pTobj->id, crStateDeleteTextureCallback);

        crStateGlobalSharedRelease();
    }

    crStateGlobalSharedRelease();

    if (!pMap->cEntered)
        crDemEntryFree(pDemEntry);
    else
        RTListNodeInsertAfter(&pMap->ReleasedList, &pDemEntry->Node);
}

int CrDemInit(PCR_DISPLAY_ENTRY_MAP pMap)
{
    pMap->pTexIdToDemInfoMap = crAllocHashtable();
    if (pMap->pTexIdToDemInfoMap)
    {
        RTListInit(&pMap->ReleasedList);
        return VINF_SUCCESS;
    }

    crWarning("crAllocHashtable failed");
    return VERR_NO_MEMORY;
}

void CrDemTerm(PCR_DISPLAY_ENTRY_MAP pMap)
{
    CRASSERT(RTListIsEmpty(&pMap->ReleasedList));
    CRASSERT(!pMap->cEntered);
    crFreeHashtable(pMap->pTexIdToDemInfoMap, NULL);
    pMap->pTexIdToDemInfoMap = NULL;
}

void CrDemEnter(PCR_DISPLAY_ENTRY_MAP pMap)
{
    ++pMap->cEntered;
    Assert(pMap->cEntered);
}

void CrDemLeave(PCR_DISPLAY_ENTRY_MAP pMap, PCR_DISPLAY_ENTRY pNewEntry, PCR_DISPLAY_ENTRY pReplacedEntry)
{
    Assert(pMap->cEntered);
    --pMap->cEntered;
    Assert(!pReplacedEntry || pNewEntry);
    if (pNewEntry && pReplacedEntry)
    {
        CR_DEM_ENTRY *pNewDemEntry = PCR_DEM_ENTRY_FROM_ENTRY(pNewEntry);
        CR_DEM_ENTRY *pReplacedDemEntry = PCR_DEM_ENTRY_FROM_ENTRY(pReplacedEntry);
        Assert(!RTListIsEmpty(&pMap->ReleasedList));
        Assert(!RTListIsEmpty(&pReplacedDemEntry->Node));
        Assert(RTListIsEmpty(&pNewDemEntry->Node));
        Assert(!pNewDemEntry->Entry.pvORInstance);
        if (!pNewDemEntry->Entry.pvORInstance)
        {
            pNewDemEntry->Entry.pvORInstance = pReplacedDemEntry->Entry.pvORInstance;
            pReplacedDemEntry->Entry.pvORInstance = NULL;
        }
        RTListNodeRemove(&pReplacedDemEntry->Node);
        crDemEntryFree(pReplacedDemEntry);
    }

    if (!pMap->cEntered)
    {
        CR_DEM_ENTRY *pCurEntry, *pNextEntry;
        RTListForEachSafe(&pMap->ReleasedList, pCurEntry, pNextEntry, CR_DEM_ENTRY, Node)
        {
            RTListNodeRemove(&pCurEntry->Node);
            crDemEntryFree(pCurEntry);
        }
    }
}

void CrDemEntryRelease(PCR_DISPLAY_ENTRY pEntry)
{
    CR_DEM_ENTRY *pDemEntry = PCR_DEM_ENTRY_FROM_ENTRY(pEntry);
    crDemEntryRelease(pDemEntry->pMap, pDemEntry);
}

int CrDemEntrySaveState(PCR_DISPLAY_ENTRY pEntry, PSSMHANDLE pSSM)
{
    CR_DEM_ENTRY *pDemEntry = PCR_DEM_ENTRY_FROM_ENTRY(pEntry);
    int  rc = SSMR3PutU32(pSSM, pDemEntry->pInfo->pTobj->id);
    AssertRCReturn(rc, rc);
    return rc;
}

int CrDemEntryLoadState(PCR_DISPLAY_ENTRY_MAP pMap, PCR_DISPLAY_ENTRY *ppEntry, PSSMHANDLE pSSM)
{
    uint32_t u32;
    int  rc = SSMR3GetU32(pSSM, &u32);
    AssertRCReturn(rc, rc);

    PCR_DISPLAY_ENTRY pEntry = CrDemEntryAcquire(pMap, u32, CRBLT_F_INVERT_SRC_YCOORDS);
    if (!pEntry)
    {
        crWarning("CrDemEntryAcquire failed");
        return VERR_NO_MEMORY;
    }

    *ppEntry = pEntry;
    return VINF_SUCCESS;
}

PCR_DISPLAY_ENTRY CrDemEntryAcquire(PCR_DISPLAY_ENTRY_MAP pMap, GLuint idTexture, uint32_t fFlags)
{
    CR_DEM_ENTRY *pDemEntry = NULL;

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

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

    Assert(pTobj->id == idTexture);

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

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

    pDemEntry = crDemEntryAlloc();
    if (!pDemEntry)
    {
        crWarning("crDemEntryAlloc failed allocating CR_DEM_ENTRY");
        crStateGlobalSharedRelease();
        return NULL;
    }

    CrDpEntryInit(&pDemEntry->Entry, &TextureData, fFlags, crDpEntryCEntryReleaseCB);

    CR_DEM_ENTRY_INFO *pInfo = (CR_DEM_ENTRY_INFO*)crHashtableSearch(pMap->pTexIdToDemInfoMap, pTobj->id);
    if (!pInfo)
    {
        pInfo = crDemEntryInfoAlloc();
        CRASSERT(pInfo);
        crHashtableAdd(pMap->pTexIdToDemInfoMap, pTobj->id, pInfo);
        pInfo->cEntries = 0;
        pInfo->pTobj = pTobj;
    }

    ++pInfo->cEntries;
    pDemEntry->pInfo = pInfo;
    pDemEntry->pMap = pMap;
    RTListInit(&pDemEntry->Node);

    /* just use main context info's context to hold the texture reference */
    CR_STATE_SHAREDOBJ_USAGE_SET(pTobj, cr_server.MainContextInfo.pContext);

    return &pDemEntry->Entry;
}

PCR_DISPLAY crServerDisplayGetInitialized(uint32_t idScreen)
{
    if (idScreen >= CR_MAX_GUEST_MONITORS)
    {
        crWarning("invalid idScreen %d", idScreen);
        return NULL;
    }

    if (ASMBitTest(cr_server.DisplaysInitMap, idScreen))
    {
        Assert(cr_server.aDispplays[idScreen].Mural.screenId == idScreen);
        return &cr_server.aDispplays[idScreen];
    }
    return NULL;
}

static PCR_DISPLAY crServerDisplayGet(uint32_t idScreen)
{
    if (idScreen >= CR_MAX_GUEST_MONITORS)
    {
        crWarning("invalid idScreen %d", idScreen);
        return NULL;
    }

    if (ASMBitTest(cr_server.DisplaysInitMap, idScreen))
    {
        Assert(cr_server.aDispplays[idScreen].Mural.screenId == idScreen);
        return &cr_server.aDispplays[idScreen];
    }

     int rc = CrDpInit(&cr_server.aDispplays[idScreen]);
     if (RT_SUCCESS(rc))
     {
         CrDpResize(&cr_server.aDispplays[idScreen],
                 cr_server.screen[idScreen].x, cr_server.screen[idScreen].y,
                 cr_server.screen[idScreen].w, cr_server.screen[idScreen].h);
         ASMBitSet(cr_server.DisplaysInitMap, idScreen);
         return &cr_server.aDispplays[idScreen];
     }
     else
     {
         crWarning("CrDpInit failed for screen %d", idScreen);
     }

    return NULL;
}

int crServerDisplaySaveState(PSSMHANDLE pSSM)
{
    int rc;
    int cDisplays = 0, i;
    for (i = 0; i < cr_server.screenCount; ++i)
    {
        if (ASMBitTest(cr_server.DisplaysInitMap, i) && !CrDpIsEmpty(&cr_server.aDispplays[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)
    {
        rc = SSMR3PutS32(pSSM, cr_server.screen[i].x);
        AssertRCReturn(rc, rc);

        rc = SSMR3PutS32(pSSM, cr_server.screen[i].y);
        AssertRCReturn(rc, rc);

        rc = SSMR3PutU32(pSSM, cr_server.screen[i].w);
        AssertRCReturn(rc, rc);

        rc = SSMR3PutU32(pSSM, cr_server.screen[i].h);
        AssertRCReturn(rc, rc);
    }

    for (i = 0; i < cr_server.screenCount; ++i)
    {
        if (ASMBitTest(cr_server.DisplaysInitMap, i) && !CrDpIsEmpty(&cr_server.aDispplays[i]))
        {
            rc = SSMR3PutS32(pSSM, i);
            AssertRCReturn(rc, rc);

            rc = CrDpSaveState(&cr_server.aDispplays[i], pSSM);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

int crServerDisplayLoadState(PSSMHANDLE pSSM, uint32_t u32Version)
{
    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);

    crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE);

    for (i = 0; i < cr_server.screenCount; ++i)
    {
        int32_t    x, y;
        uint32_t   w, h;
        rc = SSMR3GetS32(pSSM, &x);
        AssertRCReturn(rc, rc);

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

        rc = SSMR3GetU32(pSSM, &w);
        AssertRCReturn(rc, rc);

        rc = SSMR3GetU32(pSSM, &h);
        AssertRCReturn(rc, rc);

        rc = crVBoxServerMapScreen(i, x, y, w, h, cr_server.screen[i].winID);
        AssertRCReturn(rc, rc);
    }

    crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE);

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

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

        PCR_DISPLAY pDisplay = crServerDisplayGet((uint32_t)iScreen);
        if (!pDisplay)
        {
            crWarning("crServerDisplayGet failed");
            return VERR_GENERAL_FAILURE;
        }

        rc = CrDpLoadState(pDisplay, pSSM, u32Version);
        AssertRCReturn(rc, rc);
    }

    return VINF_SUCCESS;
}

void crServerDisplayTermAll()
{
    int i;
    for (i = 0; i < cr_server.screenCount; ++i)
    {
        if (ASMBitTest(cr_server.DisplaysInitMap, i))
        {
            CrDpTerm(&cr_server.aDispplays[i]);
            ASMBitClear(cr_server.DisplaysInitMap, i);
        }
    }
}

void CrHlpFreeTexImage(CRContext *pCurCtx, GLuint idPBO, void *pvData)
{
    if (idPBO)
    {
        cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
        if (pCurCtx)
            cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pCurCtx->bufferobject.packBuffer->hwid);
        else
            cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
    }
    else
    {
        crFree(pvData);
        if (pCurCtx && crStateIsBufferBoundForCtx(pCurCtx, GL_PIXEL_PACK_BUFFER_ARB))
            cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pCurCtx->bufferobject.packBuffer->hwid);
    }
}

void CrHlpPutTexImage(CRContext *pCurCtx, const VBOXVR_TEXTURE *pTexture, GLenum enmFormat, void *pvData)
{
    CRASSERT(pTexture->hwid);
    cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, pTexture->hwid);

    if (!pCurCtx || crStateIsBufferBoundForCtx(pCurCtx, GL_PIXEL_UNPACK_BUFFER_ARB))
    {
        cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
    }

    /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/
    cr_server.head_spu->dispatch_table.TexSubImage2D(GL_TEXTURE_2D,  0 /* level*/,  0 /*xoffset*/,  0 /*yoffset*/,  pTexture->width, pTexture->height, enmFormat, GL_UNSIGNED_BYTE, pvData);

    /*restore gl state*/
    if (pCurCtx)
    {
        CRTextureObj *pTObj;
        CRTextureLevel *pTImg;
        crStateGetTextureObjectAndImage(pCurCtx, pTexture->target, 0, &pTObj, &pTImg);

        GLuint uid = pTObj->hwid;
        cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, uid);
    }
    else
    {
        cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, 0);
    }

    if (pCurCtx && crStateIsBufferBoundForCtx(pCurCtx, GL_PIXEL_UNPACK_BUFFER_ARB))
        cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pCurCtx->bufferobject.unpackBuffer->hwid);
}

void* CrHlpGetTexImage(CRContext *pCurCtx, const VBOXVR_TEXTURE *pTexture, GLuint idPBO, GLenum enmFormat)
{
    void *pvData = NULL;
    cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, pTexture->hwid);

    if (idPBO)
    {
        cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, idPBO);
    }
    else
    {
        if (!pCurCtx || crStateIsBufferBoundForCtx(pCurCtx, GL_PIXEL_PACK_BUFFER_ARB))
        {
            cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
        }

        pvData = crAlloc(4*pTexture->width*pTexture->height);
        if (!pvData)
        {
            crWarning("Out of memory in CrHlpGetTexImage");
            return NULL;
        }
    }

    /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/
    cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, enmFormat, GL_UNSIGNED_BYTE, pvData);

    /*restore gl state*/
    if (pCurCtx)
    {
        CRTextureObj *pTObj;
        CRTextureLevel *pTImg;
        crStateGetTextureObjectAndImage(pCurCtx, pTexture->target, 0, &pTObj, &pTImg);

        GLuint uid = pTObj->hwid;
        cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, uid);
    }
    else
    {
        cr_server.head_spu->dispatch_table.BindTexture(pTexture->target, 0);
    }

    if (idPBO)
    {
        pvData = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
        if (!pvData)
        {
            crWarning("Failed to MapBuffer in CrHlpGetTexImage");
            return NULL;
        }
    }

    CRASSERT(pvData);
    return pvData;
}

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

    PCR_DISPLAY pDisplay;
    PCR_DISPLAY_ENTRY pEntry = NULL;

    if (texture)
    {
        pEntry = CrDemEntryAcquire(&cr_server.PresentTexturepMap, texture, (cfg & CR_PRESENT_FLAG_TEX_NONINVERT_YCOORD) ? 0 : CRBLT_F_INVERT_SRC_YCOORDS);
        if (!pEntry)
        {
            crWarning("CrDemEntryAcquire Failed");
            return;
        }

        pDisplay = crServerDisplayGet(idScreen);
        if (!pDisplay)
        {
            crWarning("crServerDisplayGet Failed");
            CrDemEntryRelease(pEntry);
            return;
        }
    }
    else
    {
        pDisplay = crServerDisplayGetInitialized(idScreen);
        if (!pDisplay)
        {
            /* no display initialized, and nothing to present */
            return;
        }
    }

    if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
    {
        CR_SERVER_DUMP_TEXPRESENT(&pEntry->CEntry.Tex);
    }

    CrDpEnter(pDisplay);

    if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS))
    {
        RTPOINT Point = {xPos, yPos};
        int rc = CrDpEntryRegionsAdd(pDisplay, pEntry, &Point, (uint32_t)cRects, (const RTRECT*)pRects, &cr_server.PresentTexturepMap);
        if (!RT_SUCCESS(rc))
        {
            crWarning("CrDpEntryRegionsAdd Failed rc %d", rc);
            /* no need to release anything, as CrDpEntryRegionsAdd would do everything for us as needed */
//            if (pEntry)
//                CrDemEntryRelease(pEntry);
        }
    }
    else
    {
        if (pEntry)
            CrDemEntryRelease(pEntry);
        CrDpRegionsClear(pDisplay);
    }

    CrDpLeave(pDisplay);
}
